Bug 1273265 - Allow long file names in specific cases. r=jimm
authorPaolo Amadini <paolo.mozmail@amadzone.org>
Thu, 06 Apr 2017 09:08:06 +0100
changeset 351598 af83078c48588a2b1e39cc0fb7fac250a58b107f
parent 351597 0901f80a81dbc1073f3355016119586524cf81e9
child 351599 ed9386534eceac63d8df8fa92297e796792442ce
push id31615
push userkwierso@gmail.com
push dateThu, 06 Apr 2017 22:01:48 +0000
treeherdermozilla-central@422bd63b18bc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm
bugs1273265
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1273265 - Allow long file names in specific cases. r=jimm 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
@@ -571,17 +571,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 {