Bug 1273265 - Allow long file names in specific cases. r=jimm a=lizzard
authorPaolo Amadini <paolo.mozmail@amadzone.org>
Thu, 06 Apr 2017 09:08:06 +0100
changeset 375815 143182787f1f7f0cf2d141fabc642269065e4b4d
parent 375814 ef9f29a17da06dda0a3a7660ea556ac068f16ad4
child 375816 5fcb7e7b5f2db66d890645d41cbd88ba6e72c532
push id11037
push usercbook@mozilla.com
push dateMon, 10 Apr 2017 12:29:04 +0000
treeherdermozilla-aurora@0d8df2fe95cc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm, lizzard
bugs1273265
milestone54.0a2
Bug 1273265 - Allow long file names in specific cases. r=jimm a=lizzard MozReview-Commit-ID: 3tl5Iihxs27
toolkit/components/jsdownloads/src/DownloadIntegration.jsm
toolkit/components/osfile/modules/osfile_win_back.jsm
toolkit/components/osfile/modules/osfile_win_front.jsm
--- a/toolkit/components/jsdownloads/src/DownloadIntegration.jsm
+++ b/toolkit/components/jsdownloads/src/DownloadIntegration.jsm
@@ -575,17 +575,21 @@ this.DownloadIntegration = {
         // whatever reason.
         zone = Ci.mozIDownloadPlatform.ZONE_INTERNET;
       }
       try {
         // Don't write zone IDs for Local, Intranet, or Trusted sites
         // to match Windows behavior.
         if (zone >= Ci.mozIDownloadPlatform.ZONE_INTERNET) {
           let streamPath = aDownload.target.path + ":Zone.Identifier";
-          let stream = yield OS.File.open(streamPath, { create: true });
+          let stream = yield OS.File.open(
+            streamPath,
+            { create: true },
+            { winAllowLengthBeyondMaxPathWithCaveats: true }
+          );
           try {
             yield stream.write(new TextEncoder().encode("[ZoneTransfer]\r\nZoneId=" + zone + "\r\n"));
           } finally {
             yield stream.close();
           }
         }
       } catch (ex) {
         // If writing to the stream fails, we ignore the error and continue.
--- a/toolkit/components/osfile/modules/osfile_win_back.jsm
+++ b/toolkit/components/osfile/modules/osfile_win_back.jsm
@@ -294,16 +294,25 @@
 
        libc.declareLazyFFI(SysFile, "GetCurrentDirectory",
          "GetCurrentDirectoryW", ctypes.winapi_abi,
                     /*return*/ Type.zero_or_DWORD,
                     /*length*/ Type.DWORD,
                     /*buf*/    Type.out_path
                    );
 
+       libc.declareLazyFFI(SysFile, "GetFullPathName",
+         "GetFullPathNameW", ctypes.winapi_abi,
+                    /*return*/   Type.zero_or_DWORD,
+                    /*fileName*/ Type.path,
+                    /*length*/   Type.DWORD,
+                    /*buf*/      Type.out_path,
+                    /*filePart*/ Type.DWORD
+                   );
+
        libc.declareLazyFFI(SysFile, "GetDiskFreeSpaceEx",
          "GetDiskFreeSpaceExW", ctypes.winapi_abi,
                     /*return*/ Type.zero_or_nothing,
                     /*directoryName*/ Type.path,
                     /*freeBytesForUser*/ Type.uint64_t.out_ptr,
                     /*totalBytesForUser*/ Type.uint64_t.out_ptr,
                     /*freeTotalBytesOnDrive*/ Type.uint64_t.out_ptr);
 
--- a/toolkit/components/osfile/modules/osfile_win_front.jsm
+++ b/toolkit/components/osfile/modules/osfile_win_front.jsm
@@ -338,16 +338,43 @@
        let security = options.winSecurity || null;
        let flags = options.winFlags !== undefined ? options.winFlags : DEFAULT_FLAGS;
        let template = options.winTemplate ? options.winTemplate._fd : null;
        let access;
        let disposition;
 
        mode = OS.Shared.AbstractFile.normalizeOpenMode(mode);
 
+       // The following option isn't a generic implementation of access to paths
+       // of arbitrary lengths. It allows for the specific case of writing to an
+       // Alternate Data Stream on a file whose path length is already close to
+       // MAX_PATH. This implementation is safe with a full path as input, if
+       // the first part of the path comes from local configuration and the
+       // file without the ADS was successfully opened before, so we know the
+       // path is valid.
+       if (options.winAllowLengthBeyondMaxPathWithCaveats) {
+         // Use the \\?\ syntax to allow lengths beyond MAX_PATH. This limited
+         // implementation only supports a DOS local path or UNC path as input.
+         let isUNC = path.length >= 2 && (path[0] == "\\" || path[0] == "/") &&
+                                         (path[1] == "\\" || path[1] == "/");
+         let pathToUse = "\\\\?\\" + (isUNC ? "UNC\\" + path.slice(2) : path);
+         // Use GetFullPathName to normalize slashes into backslashes. This is
+         // required because CreateFile won't do this for the \\?\ syntax.
+         let buffer_size = 512;
+         let array = new (ctypes.ArrayType(ctypes.char16_t, buffer_size))();
+         let expected_size = throw_on_zero("open",
+           WinFile.GetFullPathName(pathToUse, buffer_size, array, 0)
+         );
+         if (expected_size > buffer_size) {
+           // We don't need to allow an arbitrary path length for now.
+           throw new File.Error("open", ctypes.winLastError, path);
+         }
+         path = array.readString();
+       }
+
        if ("winAccess" in options && "winDisposition" in options) {
          access = options.winAccess;
          disposition = options.winDisposition;
        } else if (("winAccess" in options && !("winDisposition" in options))
                  ||(!("winAccess" in options) && "winDisposition" in options)) {
          throw new TypeError("OS.File.open requires either both options " +
            "winAccess and winDisposition or neither");
        } else {