Return-Path: X-Original-To: apmail-hadoop-common-commits-archive@www.apache.org Delivered-To: apmail-hadoop-common-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 9FB12EE11 for ; Wed, 6 Mar 2013 19:16:28 +0000 (UTC) Received: (qmail 65732 invoked by uid 500); 6 Mar 2013 19:16:28 -0000 Delivered-To: apmail-hadoop-common-commits-archive@hadoop.apache.org Received: (qmail 65683 invoked by uid 500); 6 Mar 2013 19:16:28 -0000 Mailing-List: contact common-commits-help@hadoop.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: common-dev@hadoop.apache.org Delivered-To: mailing list common-commits@hadoop.apache.org Received: (qmail 65675 invoked by uid 99); 6 Mar 2013 19:16:28 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 06 Mar 2013 19:16:28 +0000 X-ASF-Spam-Status: No, hits=-1998.0 required=5.0 tests=ALL_TRUSTED,FB_GET_MEDS X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 06 Mar 2013 19:16:12 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 3DD742388C28; Wed, 6 Mar 2013 19:15:27 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: svn commit: r1453486 [5/7] - in /hadoop/common/trunk/hadoop-common-project/hadoop-common: ./ src/main/bin/ src/main/conf/ src/main/docs/src/documentation/content/xdocs/ src/main/java/ src/main/java/org/apache/hadoop/fs/ src/main/java/org/apache/hadoop/... Date: Wed, 06 Mar 2013 19:15:22 -0000 To: common-commits@hadoop.apache.org From: suresh@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20130306191527.3DD742388C28@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Added: hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/winutils/libwinutils.c URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/winutils/libwinutils.c?rev=1453486&view=auto ============================================================================== --- hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/winutils/libwinutils.c (added) +++ hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/winutils/libwinutils.c Wed Mar 6 19:15:18 2013 @@ -0,0 +1,1515 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +#pragma comment(lib, "authz.lib") +#pragma comment(lib, "netapi32.lib") +#include "winutils.h" +#include +#include + +/* + * The array of 12 months' three-letter abbreviations + */ +const LPCWSTR MONTHS[] = { L"Jan", L"Feb", L"Mar", L"Apr", L"May", L"Jun", + L"Jul", L"Aug", L"Sep", L"Oct", L"Nov", L"Dec" }; + +/* + * The WindowsAclMask and WinMasks contain the definitions used to establish + * the mapping between Unix and Windows. + * We set up the mapping with the following rules. + * 1. Everyone will have WIN_ALL permissions; + * 2. Owner will always have WIN_OWNER_SE permissions in addition; + * 2. When Unix read/write/excute permission is set on the file, the + * corresponding Windows allow ACE will be added to the file. + * More details and explaination can be found in the following white paper: + * http://technet.microsoft.com/en-us/library/bb463216.aspx + */ +const ACCESS_MASK WinMasks[WIN_MASKS_TOTAL] = +{ + /* WIN_READ */ + FILE_READ_DATA, + /* WIN_WRITE */ + FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_APPEND_DATA | FILE_WRITE_EA | + FILE_DELETE_CHILD, + /* WIN_EXECUTE */ + FILE_EXECUTE, + /* WIN_OWNER_SE */ + DELETE | WRITE_DAC | WRITE_OWNER | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES, + /* WIN_ALL */ + READ_CONTROL | FILE_READ_EA | FILE_READ_ATTRIBUTES | SYNCHRONIZE, +}; + +//---------------------------------------------------------------------------- +// Function: GetFileInformationByName +// +// Description: +// To retrieve the by handle file information given the file name +// +// Returns: +// ERROR_SUCCESS: on success +// error code: otherwise +// +// Notes: +// If followLink parameter is set to TRUE, we will follow the symbolic link +// or junction point to get the target file information. Otherwise, the +// information for the symbolic link or junction point is retrieved. +// +DWORD GetFileInformationByName( + __in LPCWSTR pathName, + __in BOOL followLink, + __out LPBY_HANDLE_FILE_INFORMATION lpFileInformation) +{ + HANDLE fileHandle = INVALID_HANDLE_VALUE; + BOOL isSymlink = FALSE; + BOOL isJunction = FALSE; + DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS; + DWORD dwErrorCode = ERROR_SUCCESS; + + assert(lpFileInformation != NULL); + + if (!followLink) + { + if ((dwErrorCode = SymbolicLinkCheck(pathName, &isSymlink)) != ERROR_SUCCESS) + return dwErrorCode; + if ((dwErrorCode = JunctionPointCheck(pathName, &isJunction)) != ERROR_SUCCESS) + return dwErrorCode; + if (isSymlink || isJunction) + dwFlagsAndAttributes |= FILE_FLAG_OPEN_REPARSE_POINT; + } + + fileHandle = CreateFileW( + pathName, + FILE_READ_ATTRIBUTES, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + dwFlagsAndAttributes, + NULL); + if (fileHandle == INVALID_HANDLE_VALUE) + { + dwErrorCode = GetLastError(); + return dwErrorCode; + } + + if (!GetFileInformationByHandle(fileHandle, lpFileInformation)) + { + dwErrorCode = GetLastError(); + CloseHandle(fileHandle); + return dwErrorCode; + } + + CloseHandle(fileHandle); + + return dwErrorCode; +} + +//---------------------------------------------------------------------------- +// Function: IsLongWindowsPath +// +// Description: +// Checks if the path is longer than MAX_PATH in which case it needs to be +// prepended with \\?\ for Windows OS to understand it. +// +// Returns: +// TRUE long path +// FALSE otherwise +static BOOL IsLongWindowsPath(__in PCWSTR path) +{ + return (wcslen(path) + 1) > MAX_PATH; +} + +//---------------------------------------------------------------------------- +// Function: IsPrefixedAlready +// +// Description: +// Checks if the given path is already prepended with \\?\. +// +// Returns: +// TRUE if yes +// FALSE otherwise +static BOOL IsPrefixedAlready(__in PCWSTR path) +{ + static const PCWSTR LongPathPrefix = L"\\\\?\\"; + size_t Prefixlen = wcslen(LongPathPrefix); + size_t i = 0; + + if (path == NULL || wcslen(path) < Prefixlen) + { + return FALSE; + } + + for (i = 0; i < Prefixlen; ++i) + { + if (path[i] != LongPathPrefix[i]) + { + return FALSE; + } + } + + return TRUE; +} + +//---------------------------------------------------------------------------- +// Function: ConvertToLongPath +// +// Description: +// Prepends the path with the \\?\ prefix if the path is longer than MAX_PATH. +// On success, newPath should be freed with LocalFree(). Given that relative +// paths cannot be longer than MAX_PATH, we will never prepend the prefix +// to relative paths. +// +// Returns: +// ERROR_SUCCESS on success +// error code on failure +DWORD ConvertToLongPath(__in PCWSTR path, __deref_out PWSTR *newPath) +{ + DWORD dwErrorCode = ERROR_SUCCESS; + static const PCWSTR LongPathPrefix = L"\\\\?\\"; + BOOL bAppendPrefix = IsLongWindowsPath(path) && !IsPrefixedAlready(path); + HRESULT hr = S_OK; + + size_t newPathLen = wcslen(path) + (bAppendPrefix ? wcslen(LongPathPrefix) : 0); + + // Allocate the buffer for the output path (+1 for terminating NULL char) + // + PWSTR newPathValue = (PWSTR)LocalAlloc(LPTR, (newPathLen + 1) * sizeof(WCHAR)); + if (newPathValue == NULL) + { + dwErrorCode = GetLastError(); + goto ConvertToLongPathExit; + } + + if (bAppendPrefix) + { + // Append the prefix to the path + // + hr = StringCchPrintfW(newPathValue, newPathLen + 1, L"%s%s", + LongPathPrefix, path); + if (FAILED(hr)) + { + dwErrorCode = HRESULT_CODE(hr); + goto ConvertToLongPathExit; + } + } + else + { + // Just copy the original value into the output path. In this scenario + // we are doing extra buffer copy. We decided to trade code simplicity + // on the call site for small performance impact (extra allocation and + // buffer copy). As paths are short, the impact is generally small. + // + hr = StringCchPrintfW(newPathValue, newPathLen + 1, L"%s", path); + if (FAILED(hr)) + { + dwErrorCode = HRESULT_CODE(hr); + goto ConvertToLongPathExit; + } + } + + *newPath = newPathValue; + +ConvertToLongPathExit: + if (dwErrorCode != ERROR_SUCCESS) + { + LocalFree(newPathValue); + *newPath = NULL; + } + + return dwErrorCode; +} + +//---------------------------------------------------------------------------- +// Function: IsDirFileInfo +// +// Description: +// Test if the given file information is a directory +// +// Returns: +// TRUE if it is a directory +// FALSE otherwise +// +// Notes: +// +BOOL IsDirFileInfo(const BY_HANDLE_FILE_INFORMATION *fileInformation) +{ + if ((fileInformation->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + == FILE_ATTRIBUTE_DIRECTORY) + return TRUE; + return FALSE; +} + +//---------------------------------------------------------------------------- +// Function: CheckFileAttributes +// +// Description: +// Check if the given file has all the given attribute(s) +// +// Returns: +// ERROR_SUCCESS on success +// error code otherwise +// +// Notes: +// +static DWORD FileAttributesCheck( + __in LPCWSTR path, __in DWORD attr, __out PBOOL res) +{ + DWORD attrs = INVALID_FILE_ATTRIBUTES; + *res = FALSE; + if ((attrs = GetFileAttributes(path)) != INVALID_FILE_ATTRIBUTES) + *res = ((attrs & attr) == attr); + else + return GetLastError(); + return ERROR_SUCCESS; +} + +//---------------------------------------------------------------------------- +// Function: IsDirectory +// +// Description: +// Check if the given file is a directory +// +// Returns: +// ERROR_SUCCESS on success +// error code otherwise +// +// Notes: +// +DWORD DirectoryCheck(__in LPCWSTR pathName, __out PBOOL res) +{ + return FileAttributesCheck(pathName, FILE_ATTRIBUTE_DIRECTORY, res); +} + +//---------------------------------------------------------------------------- +// Function: IsReparsePoint +// +// Description: +// Check if the given file is a reparse point +// +// Returns: +// ERROR_SUCCESS on success +// error code otherwise +// +// Notes: +// +static DWORD ReparsePointCheck(__in LPCWSTR pathName, __out PBOOL res) +{ + return FileAttributesCheck(pathName, FILE_ATTRIBUTE_REPARSE_POINT, res); +} + +//---------------------------------------------------------------------------- +// Function: CheckReparseTag +// +// Description: +// Check if the given file is a reparse point of the given tag. +// +// Returns: +// ERROR_SUCCESS on success +// error code otherwise +// +// Notes: +// +static DWORD ReparseTagCheck(__in LPCWSTR path, __in DWORD tag, __out PBOOL res) +{ + BOOL isReparsePoint = FALSE; + HANDLE hFind = INVALID_HANDLE_VALUE; + WIN32_FIND_DATA findData; + DWORD dwRtnCode; + + if ((dwRtnCode = ReparsePointCheck(path, &isReparsePoint)) != ERROR_SUCCESS) + return dwRtnCode; + + if (!isReparsePoint) + { + *res = FALSE; + } + else + { + if ((hFind = FindFirstFile(path, &findData)) == INVALID_HANDLE_VALUE) + { + return GetLastError(); + } + else + { + *res = (findData.dwReserved0 == tag); + FindClose(hFind); + } + } + return ERROR_SUCCESS; +} + +//---------------------------------------------------------------------------- +// Function: IsSymbolicLink +// +// Description: +// Check if the given file is a symbolic link. +// +// Returns: +// ERROR_SUCCESS on success +// error code otherwise +// +// Notes: +// +DWORD SymbolicLinkCheck(__in LPCWSTR pathName, __out PBOOL res) +{ + return ReparseTagCheck(pathName, IO_REPARSE_TAG_SYMLINK, res); +} + +//---------------------------------------------------------------------------- +// Function: IsJunctionPoint +// +// Description: +// Check if the given file is a junction point. +// +// Returns: +// ERROR_SUCCESS on success +// error code otherwise +// +// Notes: +// +DWORD JunctionPointCheck(__in LPCWSTR pathName, __out PBOOL res) +{ + return ReparseTagCheck(pathName, IO_REPARSE_TAG_MOUNT_POINT, res); +} + +//---------------------------------------------------------------------------- +// Function: GetSidFromAcctNameW +// +// Description: +// To retrieve the SID for a user account +// +// Returns: +// ERROR_SUCCESS: on success +// Other error code: otherwise +// +// Notes: +// Caller needs to destroy the memory of Sid by calling LocalFree() +// +DWORD GetSidFromAcctNameW(LPCWSTR acctName, PSID *ppSid) +{ + DWORD dwSidSize = 0; + DWORD cchDomainName = 0; + DWORD dwDomainNameSize = 0; + LPWSTR domainName = NULL; + SID_NAME_USE eSidType; + + DWORD dwErrorCode = ERROR_SUCCESS; + + // Validate the input parameters. + // + assert (acctName != NULL && ppSid != NULL); + + // Empty name is invalid. However, LookupAccountName() function will return a + // false Sid, i.e. Sid for 'BUILDIN', for an empty name instead failing. We + // report the error before calling LookupAccountName() function for this + // special case. The error code returned here is the same as the last error + // code set by LookupAccountName() function for an invalid name. + // + if (wcslen(acctName) == 0) + return ERROR_NONE_MAPPED; + + // First pass to retrieve the buffer size. + // + LookupAccountName( + NULL, // Computer name. NULL for the local computer + acctName, + NULL, // pSid. NULL to retrieve buffer size + &dwSidSize, + NULL, // Domain Name. NULL to retrieve buffer size + &cchDomainName, + &eSidType); + + if((dwErrorCode = GetLastError()) != ERROR_INSUFFICIENT_BUFFER) + { + return dwErrorCode; + } + else + { + // Reallocate memory for the buffers. + // + *ppSid = (PSID)LocalAlloc(LPTR, dwSidSize); + if (*ppSid == NULL) + { + return GetLastError(); + } + dwDomainNameSize = (cchDomainName + 1) * sizeof(wchar_t); + domainName = (LPWSTR)LocalAlloc(LPTR, dwDomainNameSize); + if (domainName == NULL) + { + return GetLastError(); + } + + // Second pass to retrieve the SID and domain name. + // + if (!LookupAccountNameW( + NULL, // Computer name. NULL for the local computer + acctName, + *ppSid, + &dwSidSize, + domainName, + &cchDomainName, + &eSidType)) + { + LocalFree(domainName); + return GetLastError(); + } + + assert(IsValidSid(*ppSid)); + } + + LocalFree(domainName); + return ERROR_SUCCESS; +} + +//---------------------------------------------------------------------------- +// Function: GetUnixAccessMask +// +// Description: +// Compute the 3 bit Unix mask for the owner, group, or, others +// +// Returns: +// The 3 bit Unix mask in INT +// +// Notes: +// +static INT GetUnixAccessMask(ACCESS_MASK Mask) +{ + static const INT exe = 0x0001; + static const INT write = 0x0002; + static const INT read = 0x0004; + INT mask = 0; + + if ((Mask & WinMasks[WIN_READ]) == WinMasks[WIN_READ]) + mask |= read; + if ((Mask & WinMasks[WIN_WRITE]) == WinMasks[WIN_WRITE]) + mask |= write; + if ((Mask & WinMasks[WIN_EXECUTE]) == WinMasks[WIN_EXECUTE]) + mask |= exe; + return mask; +} + +//---------------------------------------------------------------------------- +// Function: GetAccess +// +// Description: +// Get Windows acces mask by AuthZ methods +// +// Returns: +// ERROR_SUCCESS: on success +// +// Notes: +// +static DWORD GetAccess(AUTHZ_CLIENT_CONTEXT_HANDLE hAuthzClient, + PSECURITY_DESCRIPTOR psd, PACCESS_MASK pAccessRights) +{ + AUTHZ_ACCESS_REQUEST AccessRequest = {0}; + AUTHZ_ACCESS_REPLY AccessReply = {0}; + BYTE Buffer[1024]; + + assert (pAccessRights != NULL); + + // Do AccessCheck + AccessRequest.DesiredAccess = MAXIMUM_ALLOWED; + AccessRequest.PrincipalSelfSid = NULL; + AccessRequest.ObjectTypeList = NULL; + AccessRequest.ObjectTypeListLength = 0; + AccessRequest.OptionalArguments = NULL; + + RtlZeroMemory(Buffer, sizeof(Buffer)); + AccessReply.ResultListLength = 1; + AccessReply.GrantedAccessMask = (PACCESS_MASK) (Buffer); + AccessReply.Error = (PDWORD) (Buffer + sizeof(ACCESS_MASK)); + + if (!AuthzAccessCheck(0, + hAuthzClient, + &AccessRequest, + NULL, + psd, + NULL, + 0, + &AccessReply, + NULL)) + { + return GetLastError(); + } + *pAccessRights = (*(PACCESS_MASK)(AccessReply.GrantedAccessMask)); + return ERROR_SUCCESS; +} + +//---------------------------------------------------------------------------- +// Function: GetEffectiveRightsForSid +// +// Description: +// Get Windows acces mask by AuthZ methods +// +// Returns: +// ERROR_SUCCESS: on success +// +// Notes: +// We run into problems for local user accounts when using the method +// GetEffectiveRightsFromAcl(). We resort to using AuthZ methods as +// an alternative way suggested on MSDN: +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa446637.aspx +// +static DWORD GetEffectiveRightsForSid(PSECURITY_DESCRIPTOR psd, + PSID pSid, + PACCESS_MASK pAccessRights) +{ + AUTHZ_RESOURCE_MANAGER_HANDLE hManager; + LUID unusedId = { 0 }; + AUTHZ_CLIENT_CONTEXT_HANDLE hAuthzClientContext = NULL; + DWORD dwRtnCode = ERROR_SUCCESS; + DWORD ret = ERROR_SUCCESS; + + assert (pAccessRights != NULL); + + if (!AuthzInitializeResourceManager(AUTHZ_RM_FLAG_NO_AUDIT, + NULL, NULL, NULL, NULL, &hManager)) + { + return GetLastError(); + } + + if(!AuthzInitializeContextFromSid(AUTHZ_SKIP_TOKEN_GROUPS, + pSid, hManager, NULL, unusedId, NULL, &hAuthzClientContext)) + { + ret = GetLastError(); + goto GetEffectiveRightsForSidEnd; + } + + if ((dwRtnCode = GetAccess(hAuthzClientContext, psd, pAccessRights)) + != ERROR_SUCCESS) + { + ret = dwRtnCode; + goto GetEffectiveRightsForSidEnd; + } + if (!AuthzFreeContext(hAuthzClientContext)) + { + ret = GetLastError(); + goto GetEffectiveRightsForSidEnd; + } + +GetEffectiveRightsForSidEnd: + return ret; +} + +//---------------------------------------------------------------------------- +// Function: FindFileOwnerAndPermission +// +// Description: +// Find the owner, primary group and permissions of a file object +// +// Returns: +// ERROR_SUCCESS: on success +// Error code otherwise +// +// Notes: +// - Caller needs to destroy the memeory of owner and group names by calling +// LocalFree() function. +// +// - If the user or group name does not exist, the user or group SID will be +// returned as the name. +// +DWORD FindFileOwnerAndPermission( + __in LPCWSTR pathName, + __out_opt LPWSTR *pOwnerName, + __out_opt LPWSTR *pGroupName, + __out_opt PINT pMask) +{ + DWORD dwRtnCode = 0; + + PSECURITY_DESCRIPTOR pSd = NULL; + + PSID psidOwner = NULL; + PSID psidGroup = NULL; + PSID psidEveryone = NULL; + DWORD cbSid = SECURITY_MAX_SID_SIZE; + PACL pDacl = NULL; + + ACCESS_MASK ownerAccessRights = 0; + ACCESS_MASK groupAccessRights = 0; + ACCESS_MASK worldAccessRights = 0; + + DWORD ret = ERROR_SUCCESS; + + // Do nothing if the caller request nothing + // + if (pOwnerName == NULL && pGroupName == NULL && pMask == NULL) + { + return ret; + } + + dwRtnCode = GetNamedSecurityInfo(pathName, SE_FILE_OBJECT, + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | + DACL_SECURITY_INFORMATION, + &psidOwner, &psidGroup, &pDacl, NULL, &pSd); + if (dwRtnCode != ERROR_SUCCESS) + { + ret = dwRtnCode; + goto FindFileOwnerAndPermissionEnd; + } + + if (pOwnerName != NULL) + { + dwRtnCode = GetAccntNameFromSid(psidOwner, pOwnerName); + if (dwRtnCode == ERROR_NONE_MAPPED) + { + if (!ConvertSidToStringSid(psidOwner, pOwnerName)) + { + ret = GetLastError(); + goto FindFileOwnerAndPermissionEnd; + } + } + else if (dwRtnCode != ERROR_SUCCESS) + { + ret = dwRtnCode; + goto FindFileOwnerAndPermissionEnd; + } + } + + if (pGroupName != NULL) + { + dwRtnCode = GetAccntNameFromSid(psidGroup, pGroupName); + if (dwRtnCode == ERROR_NONE_MAPPED) + { + if (!ConvertSidToStringSid(psidGroup, pGroupName)) + { + ret = GetLastError(); + goto FindFileOwnerAndPermissionEnd; + } + } + else if (dwRtnCode != ERROR_SUCCESS) + { + ret = dwRtnCode; + goto FindFileOwnerAndPermissionEnd; + } + } + + if (pMask == NULL) goto FindFileOwnerAndPermissionEnd; + + if ((dwRtnCode = GetEffectiveRightsForSid(pSd, + psidOwner, &ownerAccessRights)) != ERROR_SUCCESS) + { + ret = dwRtnCode; + goto FindFileOwnerAndPermissionEnd; + } + + if ((dwRtnCode = GetEffectiveRightsForSid(pSd, + psidGroup, &groupAccessRights)) != ERROR_SUCCESS) + { + ret = dwRtnCode; + goto FindFileOwnerAndPermissionEnd; + } + + if ((psidEveryone = LocalAlloc(LPTR, cbSid)) == NULL) + { + ret = GetLastError(); + goto FindFileOwnerAndPermissionEnd; + } + if (!CreateWellKnownSid(WinWorldSid, NULL, psidEveryone, &cbSid)) + { + ret = GetLastError(); + goto FindFileOwnerAndPermissionEnd; + } + if ((dwRtnCode = GetEffectiveRightsForSid(pSd, + psidEveryone, &worldAccessRights)) != ERROR_SUCCESS) + { + ret = dwRtnCode; + goto FindFileOwnerAndPermissionEnd; + } + + *pMask |= GetUnixAccessMask(ownerAccessRights) << 6; + *pMask |= GetUnixAccessMask(groupAccessRights) << 3; + *pMask |= GetUnixAccessMask(worldAccessRights); + +FindFileOwnerAndPermissionEnd: + LocalFree(psidEveryone); + LocalFree(pSd); + + return ret; +} + +//---------------------------------------------------------------------------- +// Function: GetWindowsAccessMask +// +// Description: +// Get the Windows AccessMask for user, group and everyone based on the Unix +// permission mask +// +// Returns: +// none +// +// Notes: +// none +// +static void GetWindowsAccessMask(INT unixMask, + ACCESS_MASK *userAllow, + ACCESS_MASK *userDeny, + ACCESS_MASK *groupAllow, + ACCESS_MASK *groupDeny, + ACCESS_MASK *otherAllow) +{ + assert (userAllow != NULL && userDeny != NULL && + groupAllow != NULL && groupDeny != NULL && + otherAllow != NULL); + + *userAllow = WinMasks[WIN_ALL] | WinMasks[WIN_OWNER_SE]; + if ((unixMask & UX_U_READ) == UX_U_READ) + *userAllow |= WinMasks[WIN_READ]; + + if ((unixMask & UX_U_WRITE) == UX_U_WRITE) + *userAllow |= WinMasks[WIN_WRITE]; + + if ((unixMask & UX_U_EXECUTE) == UX_U_EXECUTE) + *userAllow |= WinMasks[WIN_EXECUTE]; + + *userDeny = 0; + if ((unixMask & UX_U_READ) != UX_U_READ && + ((unixMask & UX_G_READ) == UX_G_READ || + (unixMask & UX_O_READ) == UX_O_READ)) + *userDeny |= WinMasks[WIN_READ]; + + if ((unixMask & UX_U_WRITE) != UX_U_WRITE && + ((unixMask & UX_G_WRITE) == UX_G_WRITE || + (unixMask & UX_O_WRITE) == UX_O_WRITE)) + *userDeny |= WinMasks[WIN_WRITE]; + + if ((unixMask & UX_U_EXECUTE) != UX_U_EXECUTE && + ((unixMask & UX_G_EXECUTE) == UX_G_EXECUTE || + (unixMask & UX_O_EXECUTE) == UX_O_EXECUTE)) + *userDeny |= WinMasks[WIN_EXECUTE]; + + *groupAllow = WinMasks[WIN_ALL]; + if ((unixMask & UX_G_READ) == UX_G_READ) + *groupAllow |= FILE_GENERIC_READ; + + if ((unixMask & UX_G_WRITE) == UX_G_WRITE) + *groupAllow |= WinMasks[WIN_WRITE]; + + if ((unixMask & UX_G_EXECUTE) == UX_G_EXECUTE) + *groupAllow |= WinMasks[WIN_EXECUTE]; + + *groupDeny = 0; + if ((unixMask & UX_G_READ) != UX_G_READ && + (unixMask & UX_O_READ) == UX_O_READ) + *groupDeny |= WinMasks[WIN_READ]; + + if ((unixMask & UX_G_WRITE) != UX_G_WRITE && + (unixMask & UX_O_WRITE) == UX_O_WRITE) + *groupDeny |= WinMasks[WIN_WRITE]; + + if ((unixMask & UX_G_EXECUTE) != UX_G_EXECUTE && + (unixMask & UX_O_EXECUTE) == UX_O_EXECUTE) + *groupDeny |= WinMasks[WIN_EXECUTE]; + + *otherAllow = WinMasks[WIN_ALL]; + if ((unixMask & UX_O_READ) == UX_O_READ) + *otherAllow |= WinMasks[WIN_READ]; + + if ((unixMask & UX_O_WRITE) == UX_O_WRITE) + *otherAllow |= WinMasks[WIN_WRITE]; + + if ((unixMask & UX_O_EXECUTE) == UX_O_EXECUTE) + *otherAllow |= WinMasks[WIN_EXECUTE]; +} + +//---------------------------------------------------------------------------- +// Function: GetWindowsDACLs +// +// Description: +// Get the Windows DACs based the Unix access mask +// +// Returns: +// ERROR_SUCCESS: on success +// Error code: otherwise +// +// Notes: +// - Administrators and SYSTEM are always given full permission to the file, +// unless Administrators or SYSTEM itself is the file owner and the user +// explictly set the permission to something else. For example, file 'foo' +// belongs to Administrators, 'chmod 000' on the file will not directly +// assign Administrators full permission on the file. +// - Only full permission for Administrators and SYSTEM are inheritable. +// - CREATOR OWNER is always given full permission and the permission is +// inheritable, more specifically OBJECT_INHERIT_ACE, CONTAINER_INHERIT_ACE +// flags are set. The reason is to give the creator of child file full +// permission, i.e., the child file will have permission mode 700 for +// a user other than Administrator or SYSTEM. +// +static DWORD GetWindowsDACLs(__in INT unixMask, + __in PSID pOwnerSid, __in PSID pGroupSid, __out PACL *ppNewDACL) +{ + DWORD winUserAccessDenyMask; + DWORD winUserAccessAllowMask; + DWORD winGroupAccessDenyMask; + DWORD winGroupAccessAllowMask; + DWORD winOtherAccessAllowMask; + + PSID pEveryoneSid = NULL; + DWORD cbEveryoneSidSize = SECURITY_MAX_SID_SIZE; + + PSID pSystemSid = NULL; + DWORD cbSystemSidSize = SECURITY_MAX_SID_SIZE; + BOOL bAddSystemAcls = FALSE; + + PSID pAdministratorsSid = NULL; + DWORD cbAdministratorsSidSize = SECURITY_MAX_SID_SIZE; + BOOL bAddAdministratorsAcls = FALSE; + + PSID pCreatorOwnerSid = NULL; + DWORD cbCreatorOwnerSidSize = SECURITY_MAX_SID_SIZE; + + PACL pNewDACL = NULL; + DWORD dwNewAclSize = 0; + + DWORD ret = ERROR_SUCCESS; + + GetWindowsAccessMask(unixMask, + &winUserAccessAllowMask, &winUserAccessDenyMask, + &winGroupAccessAllowMask, &winGroupAccessDenyMask, + &winOtherAccessAllowMask); + + // Create a well-known SID for the Everyone group + // + if ((pEveryoneSid = LocalAlloc(LPTR, cbEveryoneSidSize)) == NULL) + { + ret = GetLastError(); + goto GetWindowsDACLsEnd; + } + if (!CreateWellKnownSid(WinWorldSid, NULL, pEveryoneSid, &cbEveryoneSidSize)) + { + ret = GetLastError(); + goto GetWindowsDACLsEnd; + } + + // Create a well-known SID for the Administrators group + // + if ((pAdministratorsSid = LocalAlloc(LPTR, cbAdministratorsSidSize)) == NULL) + { + ret = GetLastError(); + goto GetWindowsDACLsEnd; + } + if (!CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, + pAdministratorsSid, &cbAdministratorsSidSize)) + { + ret = GetLastError(); + goto GetWindowsDACLsEnd; + } + if (!EqualSid(pAdministratorsSid, pOwnerSid) + && !EqualSid(pAdministratorsSid, pGroupSid)) + bAddAdministratorsAcls = TRUE; + + // Create a well-known SID for the SYSTEM + // + if ((pSystemSid = LocalAlloc(LPTR, cbSystemSidSize)) == NULL) + { + ret = GetLastError(); + goto GetWindowsDACLsEnd; + } + if (!CreateWellKnownSid(WinLocalSystemSid, NULL, + pSystemSid, &cbSystemSidSize)) + { + ret = GetLastError(); + goto GetWindowsDACLsEnd; + } + if (!EqualSid(pSystemSid, pOwnerSid) + && !EqualSid(pSystemSid, pGroupSid)) + bAddSystemAcls = TRUE; + + // Create a well-known SID for the Creator Owner + // + if ((pCreatorOwnerSid = LocalAlloc(LPTR, cbCreatorOwnerSidSize)) == NULL) + { + ret = GetLastError(); + goto GetWindowsDACLsEnd; + } + if (!CreateWellKnownSid(WinCreatorOwnerSid, NULL, + pCreatorOwnerSid, &cbCreatorOwnerSidSize)) + { + ret = GetLastError(); + goto GetWindowsDACLsEnd; + } + + // Create the new DACL + // + dwNewAclSize = sizeof(ACL); + dwNewAclSize += sizeof(ACCESS_ALLOWED_ACE) + + GetLengthSid(pOwnerSid) - sizeof(DWORD); + if (winUserAccessDenyMask) + dwNewAclSize += sizeof(ACCESS_DENIED_ACE) + + GetLengthSid(pOwnerSid) - sizeof(DWORD); + dwNewAclSize += sizeof(ACCESS_ALLOWED_ACE) + + GetLengthSid(pGroupSid) - sizeof(DWORD); + if (winGroupAccessDenyMask) + dwNewAclSize += sizeof(ACCESS_DENIED_ACE) + + GetLengthSid(pGroupSid) - sizeof(DWORD); + dwNewAclSize += sizeof(ACCESS_ALLOWED_ACE) + + GetLengthSid(pEveryoneSid) - sizeof(DWORD); + + if (bAddSystemAcls) + { + dwNewAclSize += sizeof(ACCESS_ALLOWED_ACE) + + cbSystemSidSize - sizeof(DWORD); + } + + if (bAddAdministratorsAcls) + { + dwNewAclSize += sizeof(ACCESS_ALLOWED_ACE) + + cbAdministratorsSidSize - sizeof(DWORD); + } + + dwNewAclSize += sizeof(ACCESS_ALLOWED_ACE) + + cbCreatorOwnerSidSize - sizeof(DWORD); + + pNewDACL = (PACL)LocalAlloc(LPTR, dwNewAclSize); + if (pNewDACL == NULL) + { + ret = GetLastError(); + goto GetWindowsDACLsEnd; + } + if (!InitializeAcl(pNewDACL, dwNewAclSize, ACL_REVISION)) + { + ret = GetLastError(); + goto GetWindowsDACLsEnd; + } + + if (!AddAccessAllowedAceEx(pNewDACL, ACL_REVISION, + CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, + GENERIC_ALL, pCreatorOwnerSid)) + { + ret = GetLastError(); + goto GetWindowsDACLsEnd; + } + + if (bAddSystemAcls && + !AddAccessAllowedAceEx(pNewDACL, ACL_REVISION, + CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, + GENERIC_ALL, pSystemSid)) + { + ret = GetLastError(); + goto GetWindowsDACLsEnd; + } + + if (bAddAdministratorsAcls && + !AddAccessAllowedAceEx(pNewDACL, ACL_REVISION, + CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, + GENERIC_ALL, pAdministratorsSid)) + { + ret = GetLastError(); + goto GetWindowsDACLsEnd; + } + + if (winUserAccessDenyMask && + !AddAccessDeniedAceEx(pNewDACL, ACL_REVISION, + NO_PROPAGATE_INHERIT_ACE, + winUserAccessDenyMask, pOwnerSid)) + { + ret = GetLastError(); + goto GetWindowsDACLsEnd; + } + if (!AddAccessAllowedAceEx(pNewDACL, ACL_REVISION, + NO_PROPAGATE_INHERIT_ACE, + winUserAccessAllowMask, pOwnerSid)) + { + ret = GetLastError(); + goto GetWindowsDACLsEnd; + } + if (winGroupAccessDenyMask && + !AddAccessDeniedAceEx(pNewDACL, ACL_REVISION, + NO_PROPAGATE_INHERIT_ACE, + winGroupAccessDenyMask, pGroupSid)) + { + ret = GetLastError(); + goto GetWindowsDACLsEnd; + } + if (!AddAccessAllowedAceEx(pNewDACL, ACL_REVISION, + NO_PROPAGATE_INHERIT_ACE, + winGroupAccessAllowMask, pGroupSid)) + { + ret = GetLastError(); + goto GetWindowsDACLsEnd; + } + if (!AddAccessAllowedAceEx(pNewDACL, ACL_REVISION, + NO_PROPAGATE_INHERIT_ACE, + winOtherAccessAllowMask, pEveryoneSid)) + { + ret = GetLastError(); + goto GetWindowsDACLsEnd; + } + + *ppNewDACL = pNewDACL; + +GetWindowsDACLsEnd: + LocalFree(pEveryoneSid); + LocalFree(pAdministratorsSid); + LocalFree(pSystemSid); + LocalFree(pCreatorOwnerSid); + if (ret != ERROR_SUCCESS) LocalFree(pNewDACL); + + return ret; +} + +//---------------------------------------------------------------------------- +// Function: ChangeFileModeByMask +// +// Description: +// Change a file or direcotry at the path to Unix mode +// +// Returns: +// ERROR_SUCCESS: on success +// Error code: otherwise +// +// Notes: +// This function is long path safe, i.e. the path will be converted to long +// path format if not already converted. So the caller does not need to do +// the converstion before calling the method. +// +DWORD ChangeFileModeByMask(__in LPCWSTR path, INT mode) +{ + LPWSTR longPathName = NULL; + PACL pNewDACL = NULL; + PSID pOwnerSid = NULL; + PSID pGroupSid = NULL; + PSECURITY_DESCRIPTOR pSD = NULL; + + SECURITY_DESCRIPTOR_CONTROL control; + DWORD revision = 0; + + PSECURITY_DESCRIPTOR pAbsSD = NULL; + PACL pAbsDacl = NULL; + PACL pAbsSacl = NULL; + PSID pAbsOwner = NULL; + PSID pAbsGroup = NULL; + + DWORD dwRtnCode = 0; + DWORD dwErrorCode = 0; + + DWORD ret = ERROR_SUCCESS; + + dwRtnCode = ConvertToLongPath(path, &longPathName); + if (dwRtnCode != ERROR_SUCCESS) + { + ret = dwRtnCode; + goto ChangeFileModeByMaskEnd; + } + + // Get owner and group Sids + // + dwRtnCode = GetNamedSecurityInfoW( + longPathName, + SE_FILE_OBJECT, + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION, + &pOwnerSid, + &pGroupSid, + NULL, + NULL, + &pSD); + if (ERROR_SUCCESS != dwRtnCode) + { + ret = dwRtnCode; + goto ChangeFileModeByMaskEnd; + } + + // SetSecurityDescriptorDacl function used below only accepts security + // descriptor in absolute format, meaning that its members must be pointers to + // other structures, rather than offsets to contiguous data. + // To determine whether a security descriptor is self-relative or absolute, + // call the GetSecurityDescriptorControl function and check the + // SE_SELF_RELATIVE flag of the SECURITY_DESCRIPTOR_CONTROL parameter. + // + if (!GetSecurityDescriptorControl(pSD, &control, &revision)) + { + ret = GetLastError(); + goto ChangeFileModeByMaskEnd; + } + + // If the security descriptor is self-relative, we use MakeAbsoluteSD function + // to convert it to absolute format. + // + if ((control & SE_SELF_RELATIVE) == SE_SELF_RELATIVE) + { + DWORD absSDSize = 0; + DWORD daclSize = 0; + DWORD saclSize = 0; + DWORD ownerSize = 0; + DWORD primaryGroupSize = 0; + MakeAbsoluteSD(pSD, NULL, &absSDSize, NULL, &daclSize, NULL, + &saclSize, NULL, &ownerSize, NULL, &primaryGroupSize); + if ((dwErrorCode = GetLastError()) != ERROR_INSUFFICIENT_BUFFER) + { + ret = dwErrorCode; + goto ChangeFileModeByMaskEnd; + } + + if ((pAbsSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, absSDSize)) == NULL) + { + ret = GetLastError(); + goto ChangeFileModeByMaskEnd; + } + if ((pAbsDacl = (PACL) LocalAlloc(LPTR, daclSize)) == NULL) + { + ret = GetLastError(); + goto ChangeFileModeByMaskEnd; + } + if ((pAbsSacl = (PACL) LocalAlloc(LPTR, saclSize)) == NULL) + { + ret = GetLastError(); + goto ChangeFileModeByMaskEnd; + } + if ((pAbsOwner = (PSID) LocalAlloc(LPTR, ownerSize)) == NULL) + { + ret = GetLastError(); + goto ChangeFileModeByMaskEnd; + } + if ((pAbsGroup = (PSID) LocalAlloc(LPTR, primaryGroupSize)) == NULL) + { + ret = GetLastError(); + goto ChangeFileModeByMaskEnd; + } + + if (!MakeAbsoluteSD(pSD, pAbsSD, &absSDSize, pAbsDacl, &daclSize, pAbsSacl, + &saclSize, pAbsOwner, &ownerSize, pAbsGroup, &primaryGroupSize)) + { + ret = GetLastError(); + goto ChangeFileModeByMaskEnd; + } + } + + // Get Windows DACLs based on Unix access mask + // + if ((dwRtnCode = GetWindowsDACLs(mode, pOwnerSid, pGroupSid, &pNewDACL)) + != ERROR_SUCCESS) + { + ret = dwRtnCode; + goto ChangeFileModeByMaskEnd; + } + + // Set the DACL information in the security descriptor; if a DACL is already + // present in the security descriptor, the DACL is replaced. The security + // descriptor is then used to set the security of a file or directory. + // + if (!SetSecurityDescriptorDacl(pAbsSD, TRUE, pNewDACL, FALSE)) + { + ret = GetLastError(); + goto ChangeFileModeByMaskEnd; + } + + // MSDN states "This function is obsolete. Use the SetNamedSecurityInfo + // function instead." However we have the following problem when using + // SetNamedSecurityInfo: + // - When PROTECTED_DACL_SECURITY_INFORMATION is not passed in as part of + // security information, the object will include inheritable permissions + // from its parent. + // - When PROTECTED_DACL_SECURITY_INFORMATION is passsed in to set + // permissions on a directory, the child object of the directory will lose + // inheritable permissions from their parent (the current directory). + // By using SetFileSecurity, we have the nice property that the new + // permissions of the object does not include the inheritable permissions from + // its parent, and the child objects will not lose their inherited permissions + // from the current object. + // + if (!SetFileSecurity(longPathName, DACL_SECURITY_INFORMATION, pAbsSD)) + { + ret = GetLastError(); + goto ChangeFileModeByMaskEnd; + } + +ChangeFileModeByMaskEnd: + LocalFree(longPathName); + LocalFree(pSD); + LocalFree(pNewDACL); + LocalFree(pAbsDacl); + LocalFree(pAbsSacl); + LocalFree(pAbsOwner); + LocalFree(pAbsGroup); + LocalFree(pAbsSD); + + return ret; +} + +//---------------------------------------------------------------------------- +// Function: GetAccntNameFromSid +// +// Description: +// To retrieve an account name given the SID +// +// Returns: +// ERROR_SUCCESS: on success +// Other error code: otherwise +// +// Notes: +// Caller needs to destroy the memory of account name by calling LocalFree() +// +DWORD GetAccntNameFromSid(PSID pSid, LPWSTR *ppAcctName) +{ + LPWSTR lpName = NULL; + DWORD cchName = 0; + LPWSTR lpDomainName = NULL; + DWORD cchDomainName = 0; + SID_NAME_USE eUse = SidTypeUnknown; + DWORD cchAcctName = 0; + DWORD dwErrorCode = ERROR_SUCCESS; + HRESULT hr = S_OK; + + DWORD ret = ERROR_SUCCESS; + + assert(ppAcctName != NULL); + + // NOTE: + // MSDN says the length returned for the buffer size including the terminating + // null character. However we found it is not true during debuging. + // + LookupAccountSid(NULL, pSid, NULL, &cchName, NULL, &cchDomainName, &eUse); + if ((dwErrorCode = GetLastError()) != ERROR_INSUFFICIENT_BUFFER) + return dwErrorCode; + lpName = (LPWSTR) LocalAlloc(LPTR, (cchName + 1) * sizeof(WCHAR)); + if (lpName == NULL) + { + ret = GetLastError(); + goto GetAccntNameFromSidEnd; + } + lpDomainName = (LPWSTR) LocalAlloc(LPTR, (cchDomainName + 1) * sizeof(WCHAR)); + if (lpDomainName == NULL) + { + ret = GetLastError(); + goto GetAccntNameFromSidEnd; + } + + if (!LookupAccountSid(NULL, pSid, + lpName, &cchName, lpDomainName, &cchDomainName, &eUse)) + { + ret = GetLastError(); + goto GetAccntNameFromSidEnd; + } + + // Buffer size = name length + 1 for '\' + domain length + 1 for NULL + cchAcctName = cchName + cchDomainName + 2; + *ppAcctName = (LPWSTR) LocalAlloc(LPTR, cchAcctName * sizeof(WCHAR)); + if (*ppAcctName == NULL) + { + ret = GetLastError(); + goto GetAccntNameFromSidEnd; + } + + hr = StringCchCopyW(*ppAcctName, cchAcctName, lpDomainName); + if (FAILED(hr)) + { + ret = HRESULT_CODE(hr); + goto GetAccntNameFromSidEnd; + } + + hr = StringCchCatW(*ppAcctName, cchAcctName, L"\\"); + if (FAILED(hr)) + { + ret = HRESULT_CODE(hr); + goto GetAccntNameFromSidEnd; + } + + hr = StringCchCatW(*ppAcctName, cchAcctName, lpName); + if (FAILED(hr)) + { + ret = HRESULT_CODE(hr); + goto GetAccntNameFromSidEnd; + } + +GetAccntNameFromSidEnd: + LocalFree(lpName); + LocalFree(lpDomainName); + if (ret != ERROR_SUCCESS) + { + LocalFree(*ppAcctName); + *ppAcctName = NULL; + } + return ret; +} + +//---------------------------------------------------------------------------- +// Function: GetLocalGroupsForUser +// +// Description: +// Get an array of groups for the given user. +// +// Returns: +// ERROR_SUCCESS on success +// Other error code on failure +// +// Notes: +// - NetUserGetLocalGroups() function only accepts full user name in the format +// [domain name]\[username]. The user input to this function can be only the +// username. In this case, NetUserGetLocalGroups() will fail on the first try, +// and we will try to find full user name using LookupAccountNameW() method, +// and call NetUserGetLocalGroups() function again with full user name. +// However, it is not always possible to find full user name given only user +// name. For example, a computer named 'win1' joined domain 'redmond' can have +// two different users, 'win1\alex' and 'redmond\alex'. Given only 'alex', we +// cannot tell which one is correct. +// +// - Caller needs to destroy the memory of groups by using the +// NetApiBufferFree() function +// +DWORD GetLocalGroupsForUser( + __in LPCWSTR user, + __out LPLOCALGROUP_USERS_INFO_0 *groups, + __out LPDWORD entries) +{ + DWORD dwEntriesRead = 0; + DWORD dwTotalEntries = 0; + NET_API_STATUS nStatus = NERR_Success; + + PSID pUserSid = NULL; + LPWSTR fullName = NULL; + + DWORD dwRtnCode = ERROR_SUCCESS; + + DWORD ret = ERROR_SUCCESS; + + *groups = NULL; + *entries = 0; + + nStatus = NetUserGetLocalGroups(NULL, + user, + 0, + 0, + (LPBYTE *) groups, + MAX_PREFERRED_LENGTH, + &dwEntriesRead, + &dwTotalEntries); + + if (nStatus == NERR_Success) + { + *entries = dwEntriesRead; + return ERROR_SUCCESS; + } + else if (nStatus != NERR_UserNotFound) + { + return nStatus; + } + + if ((dwRtnCode = GetSidFromAcctNameW(user, &pUserSid)) != ERROR_SUCCESS) + { + ret = dwRtnCode; + goto GetLocalGroupsForUserEnd; + } + + if ((dwRtnCode = GetAccntNameFromSid(pUserSid, &fullName)) != ERROR_SUCCESS) + { + ret = dwRtnCode; + goto GetLocalGroupsForUserEnd; + } + + nStatus = NetUserGetLocalGroups(NULL, + fullName, + 0, + 0, + (LPBYTE *) groups, + MAX_PREFERRED_LENGTH, + &dwEntriesRead, + &dwTotalEntries); + if (nStatus != NERR_Success) + { + // NERR_DCNotFound (2453) and NERR_UserNotFound (2221) are not published + // Windows System Error Code. All other error codes returned by + // NetUserGetLocalGroups() are valid System Error Codes according to MSDN. + ret = nStatus; + goto GetLocalGroupsForUserEnd; + } + + *entries = dwEntriesRead; + +GetLocalGroupsForUserEnd: + LocalFree(pUserSid); + LocalFree(fullName); + return ret; +} + +//---------------------------------------------------------------------------- +// Function: EnablePrivilege +// +// Description: +// Check if the process has the given privilege. If yes, enable the privilege +// to the process's access token. +// +// Returns: +// TRUE: on success +// +// Notes: +// +BOOL EnablePrivilege(__in LPCWSTR privilegeName) +{ + HANDLE hToken = INVALID_HANDLE_VALUE; + TOKEN_PRIVILEGES tp = { 0 }; + DWORD dwErrCode = ERROR_SUCCESS; + + if (!OpenProcessToken(GetCurrentProcess(), + TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) + { + ReportErrorCode(L"OpenProcessToken", GetLastError()); + return FALSE; + } + + tp.PrivilegeCount = 1; + if (!LookupPrivilegeValueW(NULL, + privilegeName, &(tp.Privileges[0].Luid))) + { + ReportErrorCode(L"LookupPrivilegeValue", GetLastError()); + CloseHandle(hToken); + return FALSE; + } + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + // As stated on MSDN, we need to use GetLastError() to check if + // AdjustTokenPrivileges() adjusted all of the specified privileges. + // + AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL); + dwErrCode = GetLastError(); + CloseHandle(hToken); + + return dwErrCode == ERROR_SUCCESS; +} + +//---------------------------------------------------------------------------- +// Function: ReportErrorCode +// +// Description: +// Report an error. Use FormatMessage function to get the system error message. +// +// Returns: +// None +// +// Notes: +// +// +void ReportErrorCode(LPCWSTR func, DWORD err) +{ + DWORD len = 0; + LPWSTR msg = NULL; + + assert(func != NULL); + + len = FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR)&msg, 0, NULL); + if (len > 0) + { + fwprintf(stderr, L"%s error (%d): %s\n", func, err, msg); + } + else + { + fwprintf(stderr, L"%s error code: %d.\n", func, err); + } + if (msg != NULL) LocalFree(msg); +} Added: hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/winutils/libwinutils.vcxproj URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/winutils/libwinutils.vcxproj?rev=1453486&view=auto ============================================================================== --- hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/winutils/libwinutils.vcxproj (added) +++ hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/winutils/libwinutils.vcxproj Wed Mar 6 19:15:18 2013 @@ -0,0 +1,171 @@ + + + + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {12131AA7-902E-4a6d-9CE3-043261D22A12} + Win32Proj + winutils + + + + StaticLibrary + true + Unicode + + + StaticLibrary + true + Unicode + + + StaticLibrary + false + true + Unicode + + + StaticLibrary + false + true + Unicode + + + + + + + + + + + + + + + + + + + include;$(IncludePath) + + + true + + + true + + ..\..\..\target\winutils\$(Configuration)\ + + + false + + + false + ..\..\..\target\bin\ + ..\..\..\target\winutils\$(Platform)\$(Configuration)\ + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + + + + + + + Level4 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + + + + + Added: hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/winutils/ls.c URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/winutils/ls.c?rev=1453486&view=auto ============================================================================== --- hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/winutils/ls.c (added) +++ hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/winutils/ls.c Wed Mar 6 19:15:18 2013 @@ -0,0 +1,346 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +#include "winutils.h" + +//---------------------------------------------------------------------------- +// Function: GetMaskString +// +// Description: +// Get the mask string that are used for output to the console. +// +// Returns: +// TRUE: on success +// +// Notes: +// The function only sets the existed permission in the mask string. If the +// permission does not exist, the corresponding character in mask string is not +// altered. The caller need to initilize the mask string to be all '-' to get +// the correct mask string. +// +static BOOL GetMaskString(INT accessMask, LPWSTR maskString) +{ + if(wcslen(maskString) != 10) + return FALSE; + + if ((accessMask & UX_DIRECTORY) == UX_DIRECTORY) + maskString[0] = L'd'; + else if ((accessMask & UX_SYMLINK) == UX_SYMLINK) + maskString[0] = L'l'; + + if ((accessMask & UX_U_READ) == UX_U_READ) + maskString[1] = L'r'; + if ((accessMask & UX_U_WRITE) == UX_U_WRITE) + maskString[2] = L'w'; + if ((accessMask & UX_U_EXECUTE) == UX_U_EXECUTE) + maskString[3] = L'x'; + + if ((accessMask & UX_G_READ) == UX_G_READ) + maskString[4] = L'r'; + if ((accessMask & UX_G_WRITE) == UX_G_WRITE) + maskString[5] = L'w'; + if ((accessMask & UX_G_EXECUTE) == UX_G_EXECUTE) + maskString[6] = L'x'; + + if ((accessMask & UX_O_READ) == UX_O_READ) + maskString[7] = L'r'; + if ((accessMask & UX_O_WRITE) == UX_O_WRITE) + maskString[8] = L'w'; + if ((accessMask & UX_O_EXECUTE) == UX_O_EXECUTE) + maskString[9] = L'x'; + + return TRUE; +} + +//---------------------------------------------------------------------------- +// Function: LsPrintLine +// +// Description: +// Print one line of 'ls' command given all the information needed +// +// Returns: +// None +// +// Notes: +// if useSeparator is false, separates the output tokens with a space +// character, otherwise, with a pipe character +// +static BOOL LsPrintLine( + const INT mask, + const DWORD hardlinkCount, + LPCWSTR ownerName, + LPCWSTR groupName, + const FILETIME *lpFileWritetime, + const LARGE_INTEGER fileSize, + LPCWSTR path, + BOOL useSeparator) +{ + // 'd' + 'rwx' for user, group, other + static const size_t ck_ullMaskLen = 1 + 3 * 3; + + LPWSTR maskString = NULL; + SYSTEMTIME stFileWriteTime; + BOOL ret = FALSE; + + maskString = (LPWSTR)LocalAlloc(LPTR, (ck_ullMaskLen+1)*sizeof(WCHAR)); + if (maskString == NULL) + { + ReportErrorCode(L"LocalAlloc", GetLastError()); + return FALSE; + } + + // Build mask string from mask mode + if (FAILED(StringCchCopyW(maskString, (ck_ullMaskLen+1), L"----------"))) + { + goto LsPrintLineEnd; + } + + if (!GetMaskString(mask, maskString)) + { + goto LsPrintLineEnd; + } + + // Convert file time to system time + if (!FileTimeToSystemTime(lpFileWritetime, &stFileWriteTime)) + { + goto LsPrintLineEnd; + } + + if (useSeparator) + { + fwprintf(stdout, L"%10s|%d|%s|%s|%lld|%3s|%2d|%4d|%s\n", + maskString, hardlinkCount, ownerName, groupName, fileSize.QuadPart, + MONTHS[stFileWriteTime.wMonth-1], stFileWriteTime.wDay, + stFileWriteTime.wYear, path); + } + else + { + fwprintf(stdout, L"%10s %d %s %s %lld %3s %2d %4d %s\n", + maskString, hardlinkCount, ownerName, groupName, fileSize.QuadPart, + MONTHS[stFileWriteTime.wMonth-1], stFileWriteTime.wDay, + stFileWriteTime.wYear, path); + } + + ret = TRUE; + +LsPrintLineEnd: + LocalFree(maskString); + + return ret; +} + +// List of command line options supported by "winutils ls" +enum CmdLineOption +{ + CmdLineOptionFollowSymlink = 0x1, // "-L" + CmdLineOptionSeparator = 0x2 // "-F" + // options should be powers of 2 (aka next is 0x4) +}; + +static wchar_t* CurrentDir = L"."; + +//---------------------------------------------------------------------------- +// Function: ParseCommandLine +// +// Description: +// Parses the command line +// +// Returns: +// TRUE on the valid command line, FALSE otherwise +// +BOOL ParseCommandLine( + int argc, wchar_t *argv[], wchar_t** path, int *optionsMask) +{ + int MaxOptions = 2; // Should be equal to the number of elems in CmdLineOption + int i = 0; + + assert(optionsMask != NULL); + assert(argv != NULL); + assert(path != NULL); + + *optionsMask = 0; + + if (argc == 1) + { + // no path specified, assume "." + *path = CurrentDir; + return TRUE; + } + + if (argc == 2) + { + // only path specified, no other options + *path = argv[1]; + return TRUE; + } + + if (argc > 2 + MaxOptions) + { + // too many parameters + return FALSE; + } + + for (i = 1; i < argc - 1; ++i) + { + if (wcscmp(argv[i], L"-L") == 0) + { + // Check if this option was already specified + BOOL alreadySet = *optionsMask & CmdLineOptionFollowSymlink; + if (alreadySet) + return FALSE; + + *optionsMask |= CmdLineOptionFollowSymlink; + } + else if (wcscmp(argv[i], L"-F") == 0) + { + // Check if this option was already specified + BOOL alreadySet = *optionsMask & CmdLineOptionSeparator; + if (alreadySet) + return FALSE; + + *optionsMask |= CmdLineOptionSeparator; + } + else + { + return FALSE; + } + } + + *path = argv[argc - 1]; + + return TRUE; +} + +//---------------------------------------------------------------------------- +// Function: Ls +// +// Description: +// The main method for ls command +// +// Returns: +// 0: on success +// +// Notes: +// +int Ls(int argc, wchar_t *argv[]) +{ + LPWSTR pathName = NULL; + LPWSTR longPathName = NULL; + + BY_HANDLE_FILE_INFORMATION fileInformation; + + LPWSTR ownerName = NULL; + LPWSTR groupName = NULL; + INT unixAccessMode = 0; + DWORD dwErrorCode = ERROR_SUCCESS; + + LARGE_INTEGER fileSize; + + BOOL isSymlink = FALSE; + + int ret = EXIT_FAILURE; + int optionsMask = 0; + + if (!ParseCommandLine(argc, argv, &pathName, &optionsMask)) + { + fwprintf(stderr, L"Incorrect command line arguments.\n\n"); + LsUsage(argv[0]); + return EXIT_FAILURE; + } + + assert(pathName != NULL); + + if (wcsspn(pathName, L"/?|><:*\"") != 0) + { + fwprintf(stderr, L"Incorrect file name format: %s\n", pathName); + return EXIT_FAILURE; + } + + // Convert the path the the long path + // + dwErrorCode = ConvertToLongPath(pathName, &longPathName); + if (dwErrorCode != ERROR_SUCCESS) + { + ReportErrorCode(L"ConvertToLongPath", dwErrorCode); + goto LsEnd; + } + + dwErrorCode = GetFileInformationByName( + longPathName, optionsMask & CmdLineOptionFollowSymlink, &fileInformation); + if (dwErrorCode != ERROR_SUCCESS) + { + ReportErrorCode(L"GetFileInformationByName", dwErrorCode); + goto LsEnd; + } + + dwErrorCode = SymbolicLinkCheck(longPathName, &isSymlink); + if (dwErrorCode != ERROR_SUCCESS) + { + ReportErrorCode(L"IsSymbolicLink", dwErrorCode); + goto LsEnd; + } + + if (isSymlink) + unixAccessMode |= UX_SYMLINK; + else if (IsDirFileInfo(&fileInformation)) + unixAccessMode |= UX_DIRECTORY; + + dwErrorCode = FindFileOwnerAndPermission(longPathName, + &ownerName, &groupName, &unixAccessMode); + if (dwErrorCode != ERROR_SUCCESS) + { + ReportErrorCode(L"FindFileOwnerAndPermission", dwErrorCode); + goto LsEnd; + } + + fileSize.HighPart = fileInformation.nFileSizeHigh; + fileSize.LowPart = fileInformation.nFileSizeLow; + + // Print output using the input path name (not the long one) + // + if (!LsPrintLine(unixAccessMode, + fileInformation.nNumberOfLinks, + ownerName, groupName, + &fileInformation.ftLastWriteTime, + fileSize, + pathName, + optionsMask & CmdLineOptionSeparator)) + goto LsEnd; + + ret = EXIT_SUCCESS; + +LsEnd: + LocalFree(ownerName); + LocalFree(groupName); + LocalFree(longPathName); + + return ret; +} + +void LsUsage(LPCWSTR program) +{ + fwprintf(stdout, L"\ +Usage: %s [OPTIONS] [FILE]\n\ +List information about the FILE (the current directory by default).\n\ +Using long listing format and list directory entries instead of contents,\n\ +and do not dereference symbolic links.\n\ +Provides equivalent or similar function as 'ls -ld' on GNU/Linux.\n\ +\n\ +OPTIONS: -L dereference symbolic links\n\ + -F format the output by separating tokens with a pipe\n", +program); +} Added: hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/winutils/main.c URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/winutils/main.c?rev=1453486&view=auto ============================================================================== --- hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/winutils/main.c (added) +++ hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/winutils/main.c Wed Mar 6 19:15:18 2013 @@ -0,0 +1,115 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +#include "winutils.h" + +static void Usage(LPCWSTR program); + +int wmain(int argc, wchar_t* argv[]) +{ + LPCWSTR cmd = NULL; + + if (argc < 2) + { + Usage(argv[0]); + return EXIT_FAILURE; + } + + cmd = argv[1]; + + if (wcscmp(L"ls", cmd) == 0) + { + return Ls(argc - 1, argv + 1); + } + else if (wcscmp(L"chmod", cmd) == 0) + { + return Chmod(argc - 1, argv + 1); + } + else if (wcscmp(L"chown", cmd) == 0) + { + return Chown(argc - 1, argv + 1); + } + else if (wcscmp(L"groups", cmd) == 0) + { + return Groups(argc - 1, argv + 1); + } + else if (wcscmp(L"hardlink", cmd) == 0) + { + return Hardlink(argc - 1, argv + 1); + } + else if (wcscmp(L"symlink", cmd) == 0) + { + return Symlink(argc - 1, argv + 1); + } + else if (wcscmp(L"task", cmd) == 0) + { + return Task(argc - 1, argv + 1); + } + else if (wcscmp(L"systeminfo", cmd) == 0) + { + return SystemInfo(); + } + else if (wcscmp(L"help", cmd) == 0) + { + Usage(argv[0]); + return EXIT_SUCCESS; + } + else + { + Usage(argv[0]); + return EXIT_FAILURE; + } +} + +static void Usage(LPCWSTR program) +{ + fwprintf(stdout, L"Usage: %s [command] ...\n\ +Provide basic command line utilities for Hadoop on Windows.\n\n\ +The available commands and their usages are:\n\n", program); + + fwprintf(stdout, L"%-15s%s\n\n", L"chmod", L"Change file mode bits."); + ChmodUsage(L"chmod"); + fwprintf(stdout, L"\n\n"); + + fwprintf(stdout, L"%-15s%s\n\n", L"chown", L"Change file owner."); + ChownUsage(L"chown"); + fwprintf(stdout, L"\n\n"); + + fwprintf(stdout, L"%-15s%s\n\n", L"groups", L"List user groups."); + GroupsUsage(L"groups"); + fwprintf(stdout, L"\n\n"); + + fwprintf(stdout, L"%-15s%s\n\n", L"hardlink", L"Hard link operations."); + HardlinkUsage(); + fwprintf(stdout, L"\n\n"); + + fwprintf(stdout, L"%-15s%s\n\n", L"ls", L"List file information."); + LsUsage(L"ls"); + fwprintf(stdout, L"\n\n"); + + fwprintf(stdout, L"%-10s%s\n\n", L"symlink", L"Create a symbolic link."); + SymlinkUsage(); + fwprintf(stdout, L"\n\n"); + + fwprintf(stdout, L"%-15s%s\n\n", L"systeminfo", L"System information."); + SystemInfoUsage(); + fwprintf(stdout, L"\n\n"); + + fwprintf(stdout, L"%-15s%s\n\n", L"task", L"Task operations."); + TaskUsage(); + fwprintf(stdout, L"\n\n"); +} Added: hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/winutils/symlink.c URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/winutils/symlink.c?rev=1453486&view=auto ============================================================================== --- hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/winutils/symlink.c (added) +++ hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/winutils/symlink.c Wed Mar 6 19:15:18 2013 @@ -0,0 +1,115 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +#include "winutils.h" + +//---------------------------------------------------------------------------- +// Function: Symlink +// +// Description: +// The main method for symlink command +// +// Returns: +// 0: on success +// +// Notes: +// +int Symlink(int argc, wchar_t *argv[]) +{ + PWSTR longLinkName = NULL; + PWSTR longFileName = NULL; + DWORD dwErrorCode = ERROR_SUCCESS; + + BOOL isDir = FALSE; + + DWORD dwRtnCode = ERROR_SUCCESS; + DWORD dwFlag = 0; + + int ret = SUCCESS; + + if (argc != 3) + { + SymlinkUsage(); + return FAILURE; + } + + dwErrorCode = ConvertToLongPath(argv[1], &longLinkName); + if (dwErrorCode != ERROR_SUCCESS) + { + ret = FAILURE; + goto SymlinkEnd; + } + dwErrorCode = ConvertToLongPath(argv[2], &longFileName); + if (dwErrorCode != ERROR_SUCCESS) + { + ret = FAILURE; + goto SymlinkEnd; + } + + // Check if the the process's access token has the privilege to create + // symbolic links. Without this step, the call to CreateSymbolicLink() from + // users have the privilege to create symbolic links will still succeed. + // This is just an additional step to do the privilege check by not using + // error code from CreateSymbolicLink() method. + // + if (!EnablePrivilege(L"SeCreateSymbolicLinkPrivilege")) + { + fwprintf(stderr, + L"No privilege to create symbolic links.\n"); + ret = SYMLINK_NO_PRIVILEGE; + goto SymlinkEnd; + } + + if ((dwRtnCode = DirectoryCheck(longFileName, &isDir)) != ERROR_SUCCESS) + { + ReportErrorCode(L"DirectoryCheck", dwRtnCode); + ret = FAILURE; + goto SymlinkEnd; + } + + if (isDir) + dwFlag = SYMBOLIC_LINK_FLAG_DIRECTORY; + + if (!CreateSymbolicLinkW(longLinkName, longFileName, dwFlag)) + { + ReportErrorCode(L"CreateSymbolicLink", GetLastError()); + ret = FAILURE; + goto SymlinkEnd; + } + +SymlinkEnd: + LocalFree(longLinkName); + LocalFree(longFileName); + return ret; +} + +void SymlinkUsage() +{ + fwprintf(stdout, L"\ +Usage: symlink [LINKNAME] [FILENAME]\n\ +Creates a symbolic link\n\ +\n\ +0 is returned on success.\n\ +2 is returned if the user does no have privilege to create symbolic links.\n\ +1 is returned for all other errors.\n\ +\n\ +The default security settings in Windows disallow non-elevated administrators\n\ +and all non-administrators from creating symbolic links. The security settings\n\ +for symbolic links can be changed in the Local Security Policy management\n\ +console.\n"); +} + Added: hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/winutils/systeminfo.c URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/winutils/systeminfo.c?rev=1453486&view=auto ============================================================================== --- hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/winutils/systeminfo.c (added) +++ hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/winutils/systeminfo.c Wed Mar 6 19:15:18 2013 @@ -0,0 +1,120 @@ +/** +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with this +* work for additional information regarding copyright ownership. The ASF +* licenses this file to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations under +* the License. +*/ + +#include "winutils.h" +#include +#include + +#define PSAPI_VERSION 1 +#pragma comment(lib, "psapi.lib") +#pragma comment(lib, "Powrprof.lib") + +typedef struct _PROCESSOR_POWER_INFORMATION { + ULONG Number; + ULONG MaxMhz; + ULONG CurrentMhz; + ULONG MhzLimit; + ULONG MaxIdleState; + ULONG CurrentIdleState; +} PROCESSOR_POWER_INFORMATION, *PPROCESSOR_POWER_INFORMATION; + +//---------------------------------------------------------------------------- +// Function: SystemInfo +// +// Description: +// Returns the resource information about the machine +// +// Returns: +// EXIT_SUCCESS: On success +// EXIT_FAILURE: otherwise +int SystemInfo() +{ + size_t vmemSize, vmemFree, memSize, memFree; + PERFORMANCE_INFORMATION memInfo; + SYSTEM_INFO sysInfo; + FILETIME idleTimeFt, kernelTimeFt, userTimeFt; + ULARGE_INTEGER idleTime, kernelTime, userTime; + ULONGLONG cpuTimeMs; + size_t size; + LPBYTE pBuffer; + PPROCESSOR_POWER_INFORMATION ppi; + long cpuFrequencyKhz; + NTSTATUS status; + + ZeroMemory(&memInfo, sizeof(PERFORMANCE_INFORMATION)); + memInfo.cb = sizeof(PERFORMANCE_INFORMATION); + if(!GetPerformanceInfo(&memInfo, sizeof(PERFORMANCE_INFORMATION))) + { + ReportErrorCode(L"GetPerformanceInfo", GetLastError()); + return EXIT_FAILURE; + } + vmemSize = memInfo.CommitLimit*memInfo.PageSize; + vmemFree = vmemSize - memInfo.CommitTotal*memInfo.PageSize; + memSize = memInfo.PhysicalTotal*memInfo.PageSize; + memFree = memInfo.PhysicalAvailable*memInfo.PageSize; + + GetSystemInfo(&sysInfo); + + if(!GetSystemTimes(&idleTimeFt, &kernelTimeFt, &userTimeFt)) + { + ReportErrorCode(L"GetSystemTimes", GetLastError()); + return EXIT_FAILURE; + } + idleTime.HighPart = idleTimeFt.dwHighDateTime; + idleTime.LowPart = idleTimeFt.dwLowDateTime; + kernelTime.HighPart = kernelTimeFt.dwHighDateTime; + kernelTime.LowPart = kernelTimeFt.dwLowDateTime; + userTime.HighPart = userTimeFt.dwHighDateTime; + userTime.LowPart = userTimeFt.dwLowDateTime; + + cpuTimeMs = (kernelTime.QuadPart - idleTime.QuadPart + userTime.QuadPart)/10000; + + // allocate buffer to get info for each processor + size = sysInfo.dwNumberOfProcessors * sizeof(PROCESSOR_POWER_INFORMATION); + pBuffer = (BYTE*) LocalAlloc(LPTR, size); + if(!pBuffer) + { + ReportErrorCode(L"LocalAlloc", GetLastError()); + return EXIT_FAILURE; + } + status = CallNtPowerInformation(ProcessorInformation, NULL, 0, pBuffer, (long)size); + if(0 != status) + { + fwprintf_s(stderr, L"Error in CallNtPowerInformation. Err:%d\n", status); + LocalFree(pBuffer); + return EXIT_FAILURE; + } + ppi = (PPROCESSOR_POWER_INFORMATION)pBuffer; + cpuFrequencyKhz = ppi->MaxMhz*1000; + LocalFree(pBuffer); + + fwprintf_s(stdout, L"%Iu,%Iu,%Iu,%Iu,%Iu,%Iu,%Iu\n", vmemSize, memSize, vmemFree, memFree, sysInfo.dwNumberOfProcessors, cpuFrequencyKhz, cpuTimeMs); + + return EXIT_SUCCESS; +} + +void SystemInfoUsage() +{ + fwprintf(stdout, L"\ + Usage: systeminfo\n\ + Prints machine information on stdout\n\ + Comma separated list of the following values.\n\ + VirtualMemorySize(bytes),PhysicalMemorySize(bytes),\n\ + FreeVirtualMemory(bytes),FreePhysicalMemory(bytes),\n\ + NumberOfProcessors,CpuFrequency(Khz),\n\ + CpuTime(MilliSec,Kernel+User)\n"); +} \ No newline at end of file Added: hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/winutils/task.c URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/winutils/task.c?rev=1453486&view=auto ============================================================================== --- hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/winutils/task.c (added) +++ hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/winutils/task.c Wed Mar 6 19:15:18 2013 @@ -0,0 +1,461 @@ +/** +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with this +* work for additional information regarding copyright ownership. The ASF +* licenses this file to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations under +* the License. +*/ + +#include "winutils.h" +#include +#include + +#define PSAPI_VERSION 1 +#pragma comment(lib, "psapi.lib") + +#define ERROR_TASK_NOT_ALIVE 1 + +// List of different task related command line options supported by +// winutils. +typedef enum TaskCommandOptionType +{ + TaskInvalid, + TaskCreate, + TaskIsAlive, + TaskKill, + TaskProcessList +} TaskCommandOption; + +//---------------------------------------------------------------------------- +// Function: ParseCommandLine +// +// Description: +// Parses the given command line. On success, out param 'command' contains +// the user specified command. +// +// Returns: +// TRUE: If the command line is valid +// FALSE: otherwise +static BOOL ParseCommandLine(__in int argc, + __in wchar_t *argv[], + __out TaskCommandOption *command) +{ + *command = TaskInvalid; + + if (wcscmp(argv[0], L"task") != 0 ) + { + return FALSE; + } + + if (argc == 3) { + if (wcscmp(argv[1], L"isAlive") == 0) + { + *command = TaskIsAlive; + return TRUE; + } + if (wcscmp(argv[1], L"kill") == 0) + { + *command = TaskKill; + return TRUE; + } + if (wcscmp(argv[1], L"processList") == 0) + { + *command = TaskProcessList; + return TRUE; + } + } + + if (argc == 4) { + if (wcscmp(argv[1], L"create") == 0) + { + *command = TaskCreate; + return TRUE; + } + } + + return FALSE; +} + +//---------------------------------------------------------------------------- +// Function: createTask +// +// Description: +// Creates a task via a jobobject. Outputs the +// appropriate information to stdout on success, or stderr on failure. +// +// Returns: +// ERROR_SUCCESS: On success +// GetLastError: otherwise +DWORD createTask(_TCHAR* jobObjName, _TCHAR* cmdLine) +{ + DWORD err = ERROR_SUCCESS; + DWORD exitCode = EXIT_FAILURE; + STARTUPINFO si; + PROCESS_INFORMATION pi; + HANDLE jobObject = NULL; + JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = { 0 }; + + // Create un-inheritable job object handle and set job object to terminate + // when last handle is closed. So winutils.exe invocation has the only open + // job object handle. Exit of winutils.exe ensures termination of job object. + // Either a clean exit of winutils or crash or external termination. + jobObject = CreateJobObject(NULL, jobObjName); + err = GetLastError(); + if(jobObject == NULL || err == ERROR_ALREADY_EXISTS) + { + return err; + } + jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; + if(SetInformationJobObject(jobObject, + JobObjectExtendedLimitInformation, + &jeli, + sizeof(jeli)) == 0) + { + err = GetLastError(); + CloseHandle(jobObject); + return err; + } + + if(AssignProcessToJobObject(jobObject, GetCurrentProcess()) == 0) + { + err = GetLastError(); + CloseHandle(jobObject); + return err; + } + + // the child JVM uses this env var to send the task OS process identifier + // to the TaskTracker. We pass the job object name. + if(SetEnvironmentVariable(_T("JVM_PID"), jobObjName) == 0) + { + err = GetLastError(); + CloseHandle(jobObject); + return err; + } + + ZeroMemory( &si, sizeof(si) ); + si.cb = sizeof(si); + ZeroMemory( &pi, sizeof(pi) ); + if(CreateProcess(NULL, cmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi) == 0) + { + err = GetLastError(); + CloseHandle(jobObject); + return err; + } + CloseHandle(pi.hThread); + + // Wait until child process exits. + WaitForSingleObject( pi.hProcess, INFINITE ); + if(GetExitCodeProcess(pi.hProcess, &exitCode) == 0) + { + err = GetLastError(); + } + CloseHandle( pi.hProcess ); + + // Terminate job object so that all spawned processes are also killed. + // This is needed because once this process closes the handle to the job + // object and none of the spawned objects have the handle open (via + // inheritance on creation) then it will not be possible for any other external + // program (say winutils task kill) to terminate this job object via its name. + if(TerminateJobObject(jobObject, exitCode) == 0) + { + err = GetLastError(); + } + + // comes here only on failure or TerminateJobObject + CloseHandle(jobObject); + + if(err != ERROR_SUCCESS) + { + return err; + } + return exitCode; +} + +//---------------------------------------------------------------------------- +// Function: isTaskAlive +// +// Description: +// Checks if a task is alive via a jobobject. Outputs the +// appropriate information to stdout on success, or stderr on failure. +// +// Returns: +// ERROR_SUCCESS: On success +// GetLastError: otherwise +DWORD isTaskAlive(const _TCHAR* jobObjName, int* isAlive, int* procsInJob) +{ + PJOBOBJECT_BASIC_PROCESS_ID_LIST procList; + HANDLE jobObject = NULL; + int numProcs = 100; + + *isAlive = FALSE; + + jobObject = OpenJobObject(JOB_OBJECT_QUERY, FALSE, jobObjName); + + if(jobObject == NULL) + { + DWORD err = GetLastError(); + if(err == ERROR_FILE_NOT_FOUND) + { + // job object does not exist. assume its not alive + return ERROR_SUCCESS; + } + return err; + } + + procList = (PJOBOBJECT_BASIC_PROCESS_ID_LIST) LocalAlloc(LPTR, sizeof (JOBOBJECT_BASIC_PROCESS_ID_LIST) + numProcs*32); + if (!procList) + { + DWORD err = GetLastError(); + CloseHandle(jobObject); + return err; + } + if(QueryInformationJobObject(jobObject, JobObjectBasicProcessIdList, procList, sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST)+numProcs*32, NULL) == 0) + { + DWORD err = GetLastError(); + if(err != ERROR_MORE_DATA) + { + CloseHandle(jobObject); + LocalFree(procList); + return err; + } + } + + if(procList->NumberOfAssignedProcesses > 0) + { + *isAlive = TRUE; + *procsInJob = procList->NumberOfAssignedProcesses; + } + + LocalFree(procList); + + return ERROR_SUCCESS; +} + +//---------------------------------------------------------------------------- +// Function: killTask +// +// Description: +// Kills a task via a jobobject. Outputs the +// appropriate information to stdout on success, or stderr on failure. +// +// Returns: +// ERROR_SUCCESS: On success +// GetLastError: otherwise +DWORD killTask(_TCHAR* jobObjName) +{ + HANDLE jobObject = OpenJobObject(JOB_OBJECT_TERMINATE, FALSE, jobObjName); + if(jobObject == NULL) + { + DWORD err = GetLastError(); + if(err == ERROR_FILE_NOT_FOUND) + { + // job object does not exist. assume its not alive + return ERROR_SUCCESS; + } + return err; + } + + if(TerminateJobObject(jobObject, 1) == 0) + { + return GetLastError(); + } + CloseHandle(jobObject); + + return ERROR_SUCCESS; +} + +//---------------------------------------------------------------------------- +// Function: printTaskProcessList +// +// Description: +// Prints resource usage of all processes in the task jobobject +// +// Returns: +// ERROR_SUCCESS: On success +// GetLastError: otherwise +DWORD printTaskProcessList(const _TCHAR* jobObjName) +{ + DWORD i; + PJOBOBJECT_BASIC_PROCESS_ID_LIST procList; + int numProcs = 100; + HANDLE jobObject = OpenJobObject(JOB_OBJECT_QUERY, FALSE, jobObjName); + if(jobObject == NULL) + { + DWORD err = GetLastError(); + return err; + } + + procList = (PJOBOBJECT_BASIC_PROCESS_ID_LIST) LocalAlloc(LPTR, sizeof (JOBOBJECT_BASIC_PROCESS_ID_LIST) + numProcs*32); + if (!procList) + { + DWORD err = GetLastError(); + CloseHandle(jobObject); + return err; + } + while(QueryInformationJobObject(jobObject, JobObjectBasicProcessIdList, procList, sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST)+numProcs*32, NULL) == 0) + { + DWORD err = GetLastError(); + if(err != ERROR_MORE_DATA) + { + CloseHandle(jobObject); + LocalFree(procList); + return err; + } + numProcs = procList->NumberOfAssignedProcesses; + LocalFree(procList); + procList = (PJOBOBJECT_BASIC_PROCESS_ID_LIST) LocalAlloc(LPTR, sizeof (JOBOBJECT_BASIC_PROCESS_ID_LIST) + numProcs*32); + if (!procList) + { + DWORD err = GetLastError(); + CloseHandle(jobObject); + return err; + } + } + + for(i=0; iNumberOfProcessIdsInList; ++i) + { + HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, (DWORD)procList->ProcessIdList[i] ); + if( hProcess != NULL ) + { + PROCESS_MEMORY_COUNTERS_EX pmc; + if ( GetProcessMemoryInfo( hProcess, (PPROCESS_MEMORY_COUNTERS)&pmc, sizeof(pmc)) ) + { + FILETIME create, exit, kernel, user; + if( GetProcessTimes( hProcess, &create, &exit, &kernel, &user) ) + { + ULARGE_INTEGER kernelTime, userTime; + ULONGLONG cpuTimeMs; + kernelTime.HighPart = kernel.dwHighDateTime; + kernelTime.LowPart = kernel.dwLowDateTime; + userTime.HighPart = user.dwHighDateTime; + userTime.LowPart = user.dwLowDateTime; + cpuTimeMs = (kernelTime.QuadPart+userTime.QuadPart)/10000; + _ftprintf_s(stdout, TEXT("%u,%Iu,%Iu,%Iu\n"), procList->ProcessIdList[i], pmc.PrivateUsage, pmc.WorkingSetSize, cpuTimeMs); + } + } + CloseHandle( hProcess ); + } + } + + LocalFree(procList); + CloseHandle(jobObject); + + return ERROR_SUCCESS; +} + +//---------------------------------------------------------------------------- +// Function: Task +// +// Description: +// Manages a task via a jobobject (create/isAlive/kill). Outputs the +// appropriate information to stdout on success, or stderr on failure. +// +// Returns: +// ERROR_SUCCESS: On success +// Error code otherwise: otherwise +int Task(int argc, wchar_t *argv[]) +{ + DWORD dwErrorCode = ERROR_SUCCESS; + TaskCommandOption command = TaskInvalid; + + if (!ParseCommandLine(argc, argv, &command)) { + dwErrorCode = ERROR_INVALID_COMMAND_LINE; + + fwprintf(stderr, L"Incorrect command line arguments.\n\n"); + TaskUsage(); + goto TaskExit; + } + + if (command == TaskCreate) + { + // Create the task jobobject + // + dwErrorCode = createTask(argv[2], argv[3]); + if (dwErrorCode != ERROR_SUCCESS) + { + ReportErrorCode(L"createTask", dwErrorCode); + goto TaskExit; + } + } else if (command == TaskIsAlive) + { + // Check if task jobobject + // + int isAlive; + int numProcs; + dwErrorCode = isTaskAlive(argv[2], &isAlive, &numProcs); + if (dwErrorCode != ERROR_SUCCESS) + { + ReportErrorCode(L"isTaskAlive", dwErrorCode); + goto TaskExit; + } + + // Output the result + if(isAlive == TRUE) + { + fwprintf(stdout, L"IsAlive,%d\n", numProcs); + } + else + { + dwErrorCode = ERROR_TASK_NOT_ALIVE; + ReportErrorCode(L"isTaskAlive returned false", dwErrorCode); + goto TaskExit; + } + } else if (command == TaskKill) + { + // Check if task jobobject + // + dwErrorCode = killTask(argv[2]); + if (dwErrorCode != ERROR_SUCCESS) + { + ReportErrorCode(L"killTask", dwErrorCode); + goto TaskExit; + } + } else if (command == TaskProcessList) + { + // Check if task jobobject + // + dwErrorCode = printTaskProcessList(argv[2]); + if (dwErrorCode != ERROR_SUCCESS) + { + ReportErrorCode(L"printTaskProcessList", dwErrorCode); + goto TaskExit; + } + } else + { + // Should not happen + // + assert(FALSE); + } + +TaskExit: + return dwErrorCode; +} + +void TaskUsage() +{ + // Hadoop code checks for this string to determine if + // jobobject's are being used. + // ProcessTree.isSetsidSupported() + fwprintf(stdout, L"\ + Usage: task create [TASKNAME] [COMMAND_LINE] |\n\ + task isAlive [TASKNAME] |\n\ + task kill [TASKNAME]\n\ + task processList [TASKNAME]\n\ + Creates a new task jobobject with taskname\n\ + Checks if task jobobject is alive\n\ + Kills task jobobject\n\ + Prints to stdout a list of processes in the task\n\ + along with their resource usage. One process per line\n\ + and comma separated info per process\n\ + ProcessId,VirtualMemoryCommitted(bytes),\n\ + WorkingSetSize(bytes),CpuTime(Millisec,Kernel+User)\n"); +}