You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by su...@apache.org on 2013/03/06 20:15:22 UTC

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/...

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 <authz.h>
+#include <sddl.h>
+
+/*
+ * 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 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+   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.
+-->
+
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{12131AA7-902E-4a6d-9CE3-043261D22A12}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>winutils</RootNamespace>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <IncludePath>include;$(IncludePath)</IncludePath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <LinkIncremental>true</LinkIncremental>
+    <OutDir />
+    <IntDir>..\..\..\target\winutils\$(Configuration)\</IntDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <LinkIncremental>false</LinkIncremental>
+    <OutDir>..\..\..\target\bin\</OutDir>
+    <IntDir>..\..\..\target\winutils\$(Platform)\$(Configuration)\</IntDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level4</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="libwinutils.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="include/winutils.h" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>

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 <psapi.h>
+#include <PowrProf.h>
+
+#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 <errno.h>
+#include <psapi.h>
+
+#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; i<procList->NumberOfProcessIdsInList; ++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");
+}