Browse Source

Add asar-supported fs.access implementation

Kevin Sawicki 8 years ago
parent
commit
3ad5504194
2 changed files with 73 additions and 0 deletions
  1. 47 0
      lib/common/asar.js
  2. 26 0
      spec/asar-spec.js

+ 47 - 0
lib/common/asar.js

@@ -126,6 +126,19 @@
     })
   }
 
+  // Create a EACCES error.
+  const accessError = function (asarPath, filePath, callback) {
+    const error = new Error(`EACCES: permission denied, access '${filePath}'`)
+    error.code = 'EACCES'
+    error.errno = -13
+    if (typeof callback !== 'function') {
+      throw error
+    }
+    process.nextTick(function () {
+      callback(error)
+    })
+  }
+
   // Create invalid archive error.
   const invalidArchiveError = function (asarPath, callback) {
     const error = new Error(`Invalid package ${asarPath}`)
@@ -362,6 +375,40 @@
       return archive.stat(filePath) !== false
     }
 
+    const {access} = fs
+    fs.access = function (p, mode, callback) {
+      const [isAsar, asarPath, filePath] = splitPath(p)
+      if (!isAsar) {
+        return access.apply(this, arguments)
+      }
+      if (typeof mode === 'function') {
+        callback = mode
+        mode = fs.constants.F_OK
+      }
+      const archive = getOrCreateArchive(asarPath)
+      if (!archive) {
+        return invalidArchiveError(asarPath, callback)
+      }
+      const info = archive.getFileInfo(filePath)
+      if (!info) {
+        return notFoundError(asarPath, filePath, callback)
+      }
+      if (info.unpacked) {
+        const realPath = archive.copyFileOut(filePath)
+        return fs.access(realPath, mode, callback)
+      }
+      const stats = getOrCreateArchive(asarPath).stat(filePath)
+      if (!stats) {
+        return notFoundError(asarPath, filePath, callback)
+      }
+      if (mode & fs.constants.W_OK) {
+        return accessError(asarPath, filePath, callback)
+      }
+      process.nextTick(function () {
+        callback()
+      })
+    }
+
     const {readFile} = fs
     fs.readFile = function (p, options, callback) {
       const [isAsar, asarPath, filePath] = splitPath(p)

+ 26 - 0
spec/asar-spec.js

@@ -534,6 +534,32 @@ describe('asar package', function () {
       })
     })
 
+    describe('fs.access', function () {
+      it('throws an error when called with write mode', function (done) {
+        var p = path.join(fixtures, 'asar', 'a.asar', 'file1')
+        fs.access(p, fs.constants.R_OK | fs.constants.W_OK, function (err) {
+          assert.equal(err.code, 'EACCES')
+          done()
+        })
+      })
+
+      it('throws an error when called on non-existent file', function (done) {
+        var p = path.join(fixtures, 'asar', 'a.asar', 'not-exist')
+        fs.access(p, function (err) {
+          assert.equal(err.code, 'ENOENT')
+          done()
+        })
+      })
+
+      it('allows write mode for unpacked files', function (done) {
+        var p = path.join(fixtures, 'asar', 'unpack.asar', 'a.txt')
+        fs.access(p, fs.constants.R_OK | fs.constants.W_OK, function (err) {
+          assert(err == null)
+          done()
+        })
+      })
+    })
+
     describe('child_process.fork', function () {
       it('opens a normal js file', function (done) {
         var child = ChildProcess.fork(path.join(fixtures, 'asar', 'a.asar', 'ping.js'))