hadoop-common-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sur...@apache.org
Subject svn commit: r1392576 - in /hadoop/common/branches/branch-1-win/src: core/org/apache/hadoop/fs/ core/org/apache/hadoop/util/ test/org/apache/hadoop/filecache/ test/org/apache/hadoop/fs/ winutils/
Date Mon, 01 Oct 2012 20:36:29 GMT
Author: suresh
Date: Mon Oct  1 20:36:29 2012
New Revision: 1392576

URL: http://svn.apache.org/viewvc?rev=1392576&view=rev
Log:
HADOOP-8694. Add support for windows native symbolic links. Contributed by Chuan Liu.

Added:
    hadoop/common/branches/branch-1-win/src/winutils/symlink.c
Modified:
    hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/fs/FileUtil.java
    hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/util/Shell.java
    hadoop/common/branches/branch-1-win/src/test/org/apache/hadoop/filecache/TestMRWithDistributedCache.java
    hadoop/common/branches/branch-1-win/src/test/org/apache/hadoop/fs/TestFileUtil.java
    hadoop/common/branches/branch-1-win/src/winutils/chmod.c
    hadoop/common/branches/branch-1-win/src/winutils/common.c
    hadoop/common/branches/branch-1-win/src/winutils/common.h
    hadoop/common/branches/branch-1-win/src/winutils/hardlink.c
    hadoop/common/branches/branch-1-win/src/winutils/ls.c
    hadoop/common/branches/branch-1-win/src/winutils/main.c
    hadoop/common/branches/branch-1-win/src/winutils/winutils.vcxproj

Modified: hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/fs/FileUtil.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/fs/FileUtil.java?rev=1392576&r1=1392575&r2=1392576&view=diff
==============================================================================
--- hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/fs/FileUtil.java (original)
+++ hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/fs/FileUtil.java Mon Oct
 1 20:36:29 2012
@@ -39,6 +39,13 @@ import org.apache.hadoop.util.Shell.Shel
 public class FileUtil {
   private static final Log LOG = LogFactory.getLog(FileUtil.class);
 
+  /* The error code is defined in winutils to indicate insufficient
+   * privilege to create symbolic links. This value need to keep in
+   * sync with the constant of the same name in:
+   * "src\winutils\common.h"
+   * */
+  public static final int SYMLINK_NO_PRIVILEGE = 2;
+  
   /**
    * convert an array of FileStatus to an array of Path
    * 
@@ -554,115 +561,46 @@ public class FileUtil {
     }
   }
 
-  //review minwei: temp hack to copy file
-  private static void copyDirectory(String fromFileName, String toFileName)
-      throws IOException {
-
-    File fromFolder = new File(fromFileName);
-    File toFolder = new File(toFileName);
-    if (fromFolder.isFile()) {
-      copyFile(fromFileName, toFileName);
-      return;
-    }
-
-    File[] filelist = fromFolder.listFiles();
-    if (filelist == null) {
-      return;
-    }
-
-    String fromPath = fromFileName;
-    String toPath = toFileName;
-    if (!toFolder.exists())
-      toFolder.mkdirs();
-    for (int i = 0; i < filelist.length; i++) {
-      String subPath = filelist[i].getName();
-      if (filelist[i].isDirectory()) {
-        copyDirectory(fromPath + "/" + subPath, toPath + "/" + subPath);
-      } else {
-        copyFile(fromPath + "/" + subPath, toPath + "/" + subPath);
-      }
-    }
-  }
-
-  private static void copyFile(String fromFileName, String toFileName)
-      throws IOException {
-    File fromFile = new File(fromFileName);
-    File toFile = new File(toFileName);
-
-    if (!fromFile.exists())
-      throw new IOException("FileCopy: " + "no such source file: "
-                            + fromFileName);
-
-    if (fromFile.isDirectory()) {
-      copyDirectory(fromFileName, toFileName);
-      return;
-    }
-
-    if (!fromFile.canRead())
-      throw new IOException("FileCopy: " + "source file is unreadable: "
-                            + fromFileName);
-
-    // Make sure the parent directory exist for the toFileName
-    if (toFile.getParent() != null) {
-      File toFileParentDir = new File(toFile.getParent());
-      if (!toFileParentDir.exists() && !toFileParentDir.mkdirs()) {
-        throw new IOException("FileCopy: failed to create target directory: "
-                              + toFileParentDir.getPath());
-      }
-    }
-
-    InputStream from = null;
-    OutputStream to = null;
-    try {
-      from = new BufferedInputStream(new FileInputStream(fromFile));
-      to = new BufferedOutputStream(new FileOutputStream(toFile));
-      byte[] buffer = new byte[4*1024*1024];
-      int bytesRead;
-
-      while ((bytesRead = from.read(buffer)) != -1)
-        to.write(buffer, 0, bytesRead); // write
-    } finally {
-      if (from != null)
-        try {
-          from.close();
-        } catch (IOException e) {
-          ;
-        }
-      if (to != null)
-        try {
-          to.close();
-        } catch (IOException e) {
-          ;
-        }
-    }
-  }
-  
   /**
    * Create a soft link between a src and destination
-   * only on a local disk. HDFS does not support this
+   * only on a local disk. HDFS does not support this.
+   * On Windows, when symlink creation fails due to security
+   * setting, we will log a warning. The return code in this
+   * case is 2.
    * @param target the target for symlink 
    * @param linkname the symlink
    * @return value returned by the command
    */
   public static int symLink(String target, String linkname) throws IOException{
-    if (Shell.DISABLEWINDOWS_TEMPORARILY) {
-      copyFile(target, linkname);
-      return 0;
-    }
-
-    String cmd = "ln -s " + target + " " + linkname;
-    Process p = Runtime.getRuntime().exec(cmd, null);
-    int returnVal = -1;
-    try{
-      returnVal = p.waitFor();
-    } catch(InterruptedException e){
-      //do nothing as of yet
-    }
-    if (returnVal != 0) {
-      LOG.warn("Command '" + cmd + "' failed " + returnVal + 
-               " with: " + copyStderr(p));
+    // Run the input paths through Java's File so that they are converted to the
+    // native OS form. FIXME: Long term fix is to expose symLink API that
+    // accepts File instead of String, as symlinks can only be created on the
+    // local FS.
+    String[] cmd = Shell.getSymlinkCommand(new File(target).getPath(),
+        new File(linkname).getPath());
+    ShellCommandExecutor shExec = new ShellCommandExecutor(cmd);
+    try {
+      shExec.execute();
+    } catch (Shell.ExitCodeException ec) {
+      int returnVal = ec.getExitCode();
+      if (Shell.WINDOWS && returnVal == SYMLINK_NO_PRIVILEGE) {
+        LOG.warn("Fail to create symbolic links on Windows. "
+            + "The default security settings in Windows disallow non-elevated "
+            + "administrators and all non-administrators from creating symbolic links. "
+            + "This behavior can be changed in the Local Security Policy management console");
+      } else if (returnVal != 0) {
+        LOG.warn("Command '" + cmd + "' failed " + returnVal + " with: "
+            + ec.getMessage());
+      }
+      return returnVal;
+    } catch (IOException e) {
+      if (LOG.isDebugEnabled()) {
+        LOG.debug("Error while create symlink " + linkname + " to " + target
+            + "." + " Exception: " + StringUtils.stringifyException(e));
+      }
+      throw e;
     }
-    return returnVal;
+    return shExec.getExitCode();
   }
   
   private static String copyStderr(Process p) throws IOException {

Modified: hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/util/Shell.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/util/Shell.java?rev=1392576&r1=1392575&r2=1392576&view=diff
==============================================================================
--- hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/util/Shell.java (original)
+++ hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/util/Shell.java Mon Oct
 1 20:36:29 2012
@@ -102,6 +102,12 @@ abstract public class Shell {
     return (WINDOWS) ? new String[] { WINUTILS, "chown", owner }
                      : new String[] { "chown", owner };
   }
+
+  /** Return a command to create symbolic links */
+  public static String[] getSymlinkCommand(String target, String link) {
+    return WINDOWS ? new String[] { WINUTILS, "symlink", link, target }
+                   : new String[] { "ln", "-s", target, link };
+  }
   
   /** Return a command to execute the given command in OS shell.
    *  On Windows, the passed in groupId can be used to launch

Modified: hadoop/common/branches/branch-1-win/src/test/org/apache/hadoop/filecache/TestMRWithDistributedCache.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1-win/src/test/org/apache/hadoop/filecache/TestMRWithDistributedCache.java?rev=1392576&r1=1392575&r2=1392576&view=diff
==============================================================================
--- hadoop/common/branches/branch-1-win/src/test/org/apache/hadoop/filecache/TestMRWithDistributedCache.java
(original)
+++ hadoop/common/branches/branch-1-win/src/test/org/apache/hadoop/filecache/TestMRWithDistributedCache.java
Mon Oct  1 20:36:29 2012
@@ -45,6 +45,7 @@ import org.apache.hadoop.mapreduce.Job;
 import org.apache.hadoop.mapreduce.Mapper;
 import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
 import org.apache.hadoop.mapreduce.lib.output.NullOutputFormat;
+import org.apache.hadoop.util.Shell;
 
 /**
  * Tests the use of the
@@ -118,7 +119,10 @@ public class TestMRWithDistributedCache 
           context.getConfiguration().get("mapred.job.tracker"))) {
         File symlinkFile = new File("distributed.first.symlink");
         TestCase.assertTrue(symlinkFile.exists());
-        TestCase.assertEquals(1, symlinkFile.length());
+        // Java 6 File#length returns zero for symbolic links on Windows
+        // FIXME: File#length for symbolic links may be due to change in Java 7
+        int expectedValue = Shell.WINDOWS ? 0 : 1;
+        TestCase.assertEquals(expectedValue, symlinkFile.length());
       }
     }
   }

Modified: hadoop/common/branches/branch-1-win/src/test/org/apache/hadoop/fs/TestFileUtil.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1-win/src/test/org/apache/hadoop/fs/TestFileUtil.java?rev=1392576&r1=1392575&r2=1392576&view=diff
==============================================================================
--- hadoop/common/branches/branch-1-win/src/test/org/apache/hadoop/fs/TestFileUtil.java (original)
+++ hadoop/common/branches/branch-1-win/src/test/org/apache/hadoop/fs/TestFileUtil.java Mon
Oct  1 20:36:29 2012
@@ -18,6 +18,8 @@
 package org.apache.hadoop.fs;
 
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collections;
@@ -328,4 +330,36 @@ public class TestFileUtil {
     long du = FileUtil.getDU(TEST_DIR);
     Assert.assertEquals(du, 0);
   }
+
+  @Test
+  public void testSymlink() throws Exception {
+    Assert.assertFalse(del.exists());
+    del.mkdirs();
+
+    byte[] data = "testSymLink".getBytes();
+
+    File file = new File(del, FILE);
+    File link = new File(del, "_link");
+
+    //write some data to the file
+    FileOutputStream os = new FileOutputStream(file);
+    os.write(data);
+    os.close();
+
+    //create the symlink
+    FileUtil.symLink(file.getAbsolutePath(), link.getAbsolutePath());
+
+    //ensure that symlink length is correctly reported by Java
+    Assert.assertEquals(data.length, file.length());
+    Assert.assertEquals(Shell.WINDOWS ? 0 : data.length, link.length());
+
+    //ensure that we can read from link.
+    FileInputStream in = new FileInputStream(link);
+    long len = 0;
+    while (in.read() > 0) {
+      len++;
+    }
+    in.close();
+    Assert.assertEquals(data.length, len);
+  }
 }

Modified: hadoop/common/branches/branch-1-win/src/winutils/chmod.c
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1-win/src/winutils/chmod.c?rev=1392576&r1=1392575&r2=1392576&view=diff
==============================================================================
--- hadoop/common/branches/branch-1-win/src/winutils/chmod.c (original)
+++ hadoop/common/branches/branch-1-win/src/winutils/chmod.c Mon Oct  1 20:36:29 2012
@@ -199,12 +199,15 @@ static BOOL ChangeFileMode(__in LPCWSTR 
 //
 // Notes:
 //  The recursion works in the following way:
-//    - If the path is not a directory, change its mode and return;
+//    - If the path is not a directory, change its mode and return.
+//      Symbolic links and junction points are not considered as directories.
 //    - Otherwise, call the method on all its children, then change its mode.
 //
 static BOOL ChangeFileModeRecursively(__in LPCWSTR path, __in_opt USHORT mode,
   __in_opt PMODE_CHANGE_ACTION actions)
 {
+  BOOL isDir = FALSE;
+  BOOL isSymlink = FALSE;
   LPWSTR dir = NULL;
 
   size_t pathSize = 0;
@@ -215,24 +218,23 @@ static BOOL ChangeFileModeRecursively(__
   DWORD dwRtnCode = ERROR_SUCCESS;
   BOOL ret = FALSE;
 
-  BY_HANDLE_FILE_INFORMATION fileInfo;
-
-  if ((dwRtnCode = GetFileInformationByName(path, &fileInfo)) != ERROR_SUCCESS)
+  if ((dwRtnCode = DirectoryCheck(path, &isDir)) != ERROR_SUCCESS)
+  {
+    ReportErrorCode(L"IsDirectory", dwRtnCode);
+    return FALSE;
+  }
+  if ((dwRtnCode = SymbolicLinkCheck(path, &isSymlink)) != ERROR_SUCCESS)
   {
-    ReportErrorCode(L"GetFileInformationByName", dwRtnCode);
+    ReportErrorCode(L"IsSymbolicLink", dwRtnCode);
     return FALSE;
   }
 
-  if (!IsDirFileInfo(&fileInfo))
+  if (isSymlink || !isDir)
   {
      if (ChangeFileMode(path, mode, actions))
-     {
        return TRUE;
-     }
      else
-     {
        return FALSE;
-     }
   }
 
   if (FAILED(StringCchLengthW(path, STRSAFE_MAX_CCH - 3, &pathSize)))
@@ -499,7 +501,7 @@ static BOOL ParseCommandLineArguments(__
       // Check if the given path name is a file or directory
       // Only set recursive flag if the given path is a directory
       //
-      dwRtnCode = GetFileInformationByName(*path, &fileInfo);
+      dwRtnCode = GetFileInformationByName(*path, FALSE, &fileInfo);
       if (dwRtnCode != ERROR_SUCCESS)
       {
         ReportErrorCode(L"GetFileInformationByName", dwRtnCode);
@@ -698,7 +700,7 @@ static BOOL ConvertActionsToMask(__in LP
 
   USHORT mode = 0;
 
-  dwErrorCode = GetFileInformationByName(path, &fileInformation);
+  dwErrorCode = GetFileInformationByName(path, FALSE, &fileInformation);
   if (dwErrorCode != ERROR_SUCCESS)
   {
     ReportErrorCode(L"GetFileInformationByName", dwErrorCode);

Modified: hadoop/common/branches/branch-1-win/src/winutils/common.c
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1-win/src/winutils/common.c?rev=1392576&r1=1392575&r2=1392576&view=diff
==============================================================================
--- hadoop/common/branches/branch-1-win/src/winutils/common.c (original)
+++ hadoop/common/branches/branch-1-win/src/winutils/common.c Mon Oct  1 20:36:29 2012
@@ -61,23 +61,37 @@ const ACCESS_MASK WinMasks[WIN_MASKS_TOT
 //  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 = NULL;
+  HANDLE fileHandle = INVALID_HANDLE_VALUE;
+  BOOL isSymlink = 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 (isSymlink)
+      dwFlagsAndAttributes |= FILE_FLAG_OPEN_REPARSE_POINT;
+  }
+
   fileHandle = CreateFileW(
     pathName,
     FILE_READ_ATTRIBUTES,
     FILE_SHARE_READ,
     NULL,
     OPEN_EXISTING,
-    FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
+    dwFlagsAndAttributes,
     NULL);
   if (fileHandle == INVALID_HANDLE_VALUE)
   {
@@ -199,6 +213,122 @@ BOOL IsDirFileInfo(const BY_HANDLE_FILE_
 }
 
 //----------------------------------------------------------------------------
+// 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: GetSidFromAcctNameW
 //
 // Description:

Modified: hadoop/common/branches/branch-1-win/src/winutils/common.h
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1-win/src/winutils/common.h?rev=1392576&r1=1392575&r2=1392576&view=diff
==============================================================================
--- hadoop/common/branches/branch-1-win/src/winutils/common.h (original)
+++ hadoop/common/branches/branch-1-win/src/winutils/common.h Mon Oct  1 20:36:29 2012
@@ -28,6 +28,16 @@
 #include <accctrl.h>
 #include <tchar.h>
 #include <strsafe.h>
+enum EXIT_CODE
+{
+  /* Common success exit code shared among all utilities */
+  SUCCESS = EXIT_SUCCESS,
+  /* Generic failure exit code share among all utilities */
+  FAILURE = EXIT_FAILURE,
+  /* Failure code indicates the user does not privilege to create symlinks */
+  SYMLINK_NO_PRIVILEGE = 2,
+};
+
 
 /*
  * The array of 12 months' three-letter abbreviations 
@@ -49,6 +59,7 @@ enum UnixAclMask
   UX_U_WRITE   = 0x0080,
   UX_U_READ    = 0x0100,
   UX_DIRECTORY = 0x0200,
+  UX_SYMLINK   = 0x0400,
 };
 
 
@@ -86,10 +97,13 @@ void HardlinkUsage();
 int Task(int argc, wchar_t *argv[]);
 void TaskUsage();
 
+int Symlink(int argc, wchar_t *argv[]);
+void SymlinkUsage();
+
 int SystemInfo();
 void SystemInfoUsage();
 
-DWORD GetFileInformationByName(__in LPCWSTR pathName,
+DWORD GetFileInformationByName(__in LPCWSTR pathName,  __in BOOL followLink,
   __out LPBY_HANDLE_FILE_INFORMATION lpFileInformation);
 
 DWORD ConvertToLongPath(__in PCWSTR path, __deref_out PWSTR *newPath);
@@ -104,4 +118,8 @@ BOOL FindFileOwnerAndPermission(
   __in LPCWSTR pathName,
   __out_opt LPWSTR *pOwnerName,
   __out_opt LPWSTR *pGroupName,
-  __out_opt PUSHORT pMask);
\ No newline at end of file
+  __out_opt PUSHORT pMask);
+
+DWORD DirectoryCheck(__in LPCWSTR pathName, __out LPBOOL result);
+
+DWORD SymbolicLinkCheck(__in LPCWSTR pathName, __out LPBOOL result);

Modified: hadoop/common/branches/branch-1-win/src/winutils/hardlink.c
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1-win/src/winutils/hardlink.c?rev=1392576&r1=1392575&r2=1392576&view=diff
==============================================================================
--- hadoop/common/branches/branch-1-win/src/winutils/hardlink.c (original)
+++ hadoop/common/branches/branch-1-win/src/winutils/hardlink.c Mon Oct  1 20:36:29 2012
@@ -94,7 +94,7 @@ static DWORD HardlinkStat(__in LPCWSTR f
 
   // Get file information which contains the hard link count
   //
-  dwErrorCode = GetFileInformationByName(longFileName, &fileInformation);
+  dwErrorCode = GetFileInformationByName(longFileName, FALSE, &fileInformation);
   if (dwErrorCode != ERROR_SUCCESS)
   {
     goto HardlinkStatExit;

Modified: hadoop/common/branches/branch-1-win/src/winutils/ls.c
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1-win/src/winutils/ls.c?rev=1392576&r1=1392575&r2=1392576&view=diff
==============================================================================
--- hadoop/common/branches/branch-1-win/src/winutils/ls.c (original)
+++ hadoop/common/branches/branch-1-win/src/winutils/ls.c Mon Oct  1 20:36:29 2012
@@ -35,6 +35,8 @@ static BOOL GetMaskString(USHORT accessM
 
   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';
@@ -167,6 +169,8 @@ int Ls(int argc, wchar_t *argv[])
 
   LARGE_INTEGER fileSize;
 
+  BOOL isSymlink = FALSE;
+
   int ret = EXIT_FAILURE;
 
   if (argc > 2)
@@ -197,18 +201,25 @@ int Ls(int argc, wchar_t *argv[])
     goto LsEnd;
   }
 
-  dwErrorCode = GetFileInformationByName(longPathName, &fileInformation);
+  dwErrorCode = GetFileInformationByName(longPathName, FALSE, &fileInformation);
   if (dwErrorCode != ERROR_SUCCESS)
   {
     ReportErrorCode(L"GetFileInformationByName", dwErrorCode);
     goto LsEnd;
   }
 
-  if (IsDirFileInfo(&fileInformation))
+  dwErrorCode = SymbolicLinkCheck(pathName, &isSymlink);
+  if (dwErrorCode != ERROR_SUCCESS)
   {
-    accessMask |= UX_DIRECTORY;
+     ReportErrorCode(L"IsSymbolicLink", dwErrorCode);
+     goto LsEnd;
   }
 
+  if (isSymlink)
+    accessMask |= UX_SYMLINK;
+  else if (IsDirFileInfo(&fileInformation))
+    accessMask |= UX_DIRECTORY;
+
   if (!FindFileOwnerAndPermission(longPathName,
     &ownerName, &groupName, &accessMask))
     goto LsEnd;

Modified: hadoop/common/branches/branch-1-win/src/winutils/main.c
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1-win/src/winutils/main.c?rev=1392576&r1=1392575&r2=1392576&view=diff
==============================================================================
--- hadoop/common/branches/branch-1-win/src/winutils/main.c (original)
+++ hadoop/common/branches/branch-1-win/src/winutils/main.c Mon Oct  1 20:36:29 2012
@@ -51,6 +51,10 @@ int wmain(int argc, wchar_t* argv[])
   {
     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);
@@ -80,18 +84,14 @@ static void Usage(LPCWSTR program)
 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"ls", L"List file information.");
-  LsUsage(L"ls");
-  fwprintf(stdout, L"\n\n");
-
   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");
@@ -100,12 +100,20 @@ The available commands and their usages 
   HardlinkUsage();
   fwprintf(stdout, L"\n\n");
 
-  fwprintf(stdout, L"%-15s%s\n\n", L"task", L"Task operations.");
-  TaskUsage();
+  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"%-15s%s\n\n", L"task", L"Task operations.");
+  TaskUsage();
+  fwprintf(stdout, L"\n\n");
+
   fwprintf(stdout, L"\n\n");
-}
\ No newline at end of file
+}

Added: hadoop/common/branches/branch-1-win/src/winutils/symlink.c
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1-win/src/winutils/symlink.c?rev=1392576&view=auto
==============================================================================
--- hadoop/common/branches/branch-1-win/src/winutils/symlink.c (added)
+++ hadoop/common/branches/branch-1-win/src/winutils/symlink.c Mon Oct  1 20:36:29 2012
@@ -0,0 +1,160 @@
+/**
+ * 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 "common.h"
+
+//----------------------------------------------------------------------------
+// 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:
+//
+static BOOL EnablePrivilege(__in LPCWSTR privilegeName)
+{
+  HANDLE hToken;
+  TOKEN_PRIVILEGES tp;
+  DWORD dwErrCode;
+
+  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: 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");
+}
+

Modified: hadoop/common/branches/branch-1-win/src/winutils/winutils.vcxproj
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1-win/src/winutils/winutils.vcxproj?rev=1392576&r1=1392575&r2=1392576&view=diff
==============================================================================
--- hadoop/common/branches/branch-1-win/src/winutils/winutils.vcxproj (original)
+++ hadoop/common/branches/branch-1-win/src/winutils/winutils.vcxproj Mon Oct  1 20:36:29
2012
@@ -135,6 +135,7 @@
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
+    <ClCompile Include="symlink.c" />
     <ClCompile Include="systeminfo.c" />
     <ClCompile Include="chmod.c" />
     <ClCompile Include="chown.c" />



Mime
View raw message