Repository: streamich/memfs
Pull request #41 (2017-09-26 19:16:13)

Make sure mode is OR'ed with the permission on init and when chmoding
The `mode` variable inside a `Node` should not only indicate the type of Node it is (directory, file, etc), but should also contain information about it's permissions.

This PR makes sure that the mode is `|=`'ed with the `perm` variable when creating a new node and when chmoding. Also adds tests.


Patch:
diff --git a/lib/node.js b/lib/node.js
index 14c7e99b..a687604d 100644
--- a/lib/node.js
+++ b/lib/node.js
@@ -38,6 +38,7 @@ var Node = (function (_super) {
         // Steps to another node, if this node is a symlink.
         _this.symlink = null;
         _this.perm = perm;
+        _this.mode |= perm;
         _this.ino = ino;
         return _this;
     }
@@ -140,6 +141,7 @@ var Node = (function (_super) {
     };
     Node.prototype.chmod = function (perm) {
         this.perm = perm;
+        this.mode |= perm;
         this.touch();
     };
     Node.prototype.chown = function (uid, gid) {
diff --git a/lib/volume.js b/lib/volume.js
index dec959f4..e8d578a6 100644
--- a/lib/volume.js
+++ b/lib/volume.js
@@ -776,6 +776,7 @@ var Volume = (function () {
             mode = 438 /* DEFAULT */;
             callback = a;
         }
+        mode = mode || 438 /* DEFAULT */;
         var modeNum = modeToNumber(mode);
         var fileName = pathToFilename(path);
         var flagsNum = flagsToNumber(flags);
diff --git a/src/node.ts b/src/node.ts
index e372666d..86c92843 100644
--- a/src/node.ts
+++ b/src/node.ts
@@ -40,6 +40,7 @@ export class Node extends EventEmitter {
     constructor(ino: number, perm: number = 0o666) {
         super();
         this.perm = perm;
+        this.mode |= perm;
         this.ino = ino;
     }
 
@@ -151,6 +152,7 @@ export class Node extends EventEmitter {
 
     chmod(perm: number) {
         this.perm = perm;
+        this.mode |= perm;
         this.touch();
     }
 
diff --git a/src/volume.ts b/src/volume.ts
index 4f5dded2..45393a3e 100644
--- a/src/volume.ts
+++ b/src/volume.ts
@@ -922,6 +922,7 @@ export class Volume {
             mode = MODE.DEFAULT;
             callback = a;
         }
+        mode = mode || MODE.DEFAULT;
 
         const modeNum = modeToNumber(mode);
         const fileName = pathToFilename(path);


Test patch:
diff --git a/src/__tests__/node.test.ts b/src/__tests__/node.test.ts
index d948c421..9f06c4ae 100644
--- a/src/__tests__/node.test.ts
+++ b/src/__tests__/node.test.ts
@@ -1,9 +1,16 @@
-import {Node} from "../node";
+import {Node, Stats} from "../node";
+import {constants, S} from '../constants';
 
 
 describe('node.ts', () => {
     describe('Node', () => {
         const node = new Node(1);
+        it('properly sets mode with permission respected', () => {
+          const node = new Node(1, 0o755);
+          expect(node.perm).toBe(0o755);
+          expect(node.mode).toBe(constants.S_IFREG | 0o755);
+          expect(node.isFile()).toBe(true); // Make sure we still know it's a file
+        });
         it('Setting/getting buffer creates a copy', () => {
             const buf = Buffer.from([1,2,3]);
             node.setBuffer(buf);
@@ -55,5 +62,13 @@ describe('node.ts', () => {
                 expect(buf.equals(Buffer.from([2]))).toBe(true);
             });
         });
+        describe('.chmod(perm)', () => {
+          const node = new Node(1);
+          expect(node.perm).toBe(0o666);
+          expect(node.isFile()).toBe(true);
+          node.chmod(0o600);
+          expect(node.perm).toBe(0o600);
+          expect(node.isFile()).toBe(true);
+        });
     });
 });
\ No newline at end of file
diff --git a/src/__tests__/volume.test.ts b/src/__tests__/volume.test.ts
index dba5dd70..f3629fbd 100644
--- a/src/__tests__/volume.test.ts
+++ b/src/__tests__/volume.test.ts
@@ -327,6 +327,18 @@ describe('volume', () => {
                     done();
                 }
             });
+            it('Properly sets permissions from mode when creating a new file', done => {
+              vol.writeFileSync('/a.txt', 'foo');
+              const stats = vol.statSync('/a.txt');
+              // Write a new file, copying the mode from the old file
+              vol.open('/b.txt', 'w', stats.mode, (err, fd) => {
+                  expect(err).toBe(null);
+                  expect(vol.root.getChild('b.txt')).toBeInstanceOf(Link);
+                  expect(typeof fd).toBe('number');
+                  expect(vol.root.getChild('b.txt').getNode().canWrite()).toBe(true);
+                  done();
+              });
+            })
         });
         describe('.close(fd, callback)', () => {
             const vol = new Volume;