Categories
JavaScript Nodejs

Node.js FS Module — Changing File Permissions and Ownership with File Descriptors

Spread the love

Manipulating files and directories are basic operations for any program. Since Node.js is a server side platform and can interact with the computer that it’s running on directly, being able to manipulate files is a basic feature. Fortunately, Node.js has a fs module built into its library. It has many functions that can help with manipulating files and folders. File and directory operation that are supported include basic ones like manipulating and opening files in directories. Likewise, it can do the same for files. It can do this both synchronously and asynchronously. It has an asynchronous API that have functions that support promises. Also it can show statistics for a file. Almost all the file operations that we can think of can be done with the built in fs module. In this article, we will get the file descriptor of a file and then use that to manipulate the permissions with the fchmod family of functions and change ownership of the file with the fchown family of functions.

Get the File Descriptor with the Open Function

A file descriptor of a file is an unique identifier of a file which we can use to do various operations to it. To get the file descriptor of a file, we can use the promise version of the open function and all its variants to do so. The promise version of the open function takes 2 arguments. The first is the file path, which can be a string, a Buffer object or an URL object. The second is the flag for what can our program do with the file after it’s opened. They can be one of the following strings:

  • 'a' – Opens a file for appending, which means adding data to the existing file. The file is created if it does not exist.
  • 'ax' – Like 'a' but exception thrown if the path exists.
  • 'a+' – Open file for reading and appending. The file is created if it doesn’t exist.
  • 'ax+' – Like 'a+' but exception thrown if the path exists.
  • 'as' – Opens a file for appending in synchronous mode. The file is created if it does not exist.
  • 'as+' – Opens a file for reading and appending in synchronous mode. The file is created if it does not exist.
  • 'r' – Opens a file for reading. An exception is thrown if the file doesn’t exist.
  • 'r+' – Opens a file for reading and writing. An exception is thrown if the file doesn’t exist.
  • 'rs+' – Opens a file for reading and writing in synchronous mode.
  • 'w' – Opens a file for writing. The file is created (if it does not exist) or overwritten (if it exists).
  • 'wx' – Like 'w' but fails if the path exists.
  • 'w+' – Opens a file for reading and writing. The file is created (if it does not exist) or overwritten (if it exists).
  • 'wx+' – Like 'w+' but exception thrown if the path exists.

The promise version of the open function resolves to a file handle, which has the fd property with the file descriptor.

Changing File Permission of File Referenced by the File Descriptor with the fs.fchmod Function

After we got the file descriptor, we can use it to change the permission of the file that’s identified by the file descriptor. To do so, we use the fchmod function. The function takes 3 arguments. The first is the file descriptor, which is an integer. The second is the mode, which is the permission and sticky bits we want to change for the file. The mode consists of 3 octal digits, like with Unix or Linux file permissions. The leftmost digit is the permission for the owner. The middle digit is the permission of the group, and the rightmost digit is the permission for others. Valid number include the following:

  • 7 — read, write, and execute
  • 6 — read and write
  • 5 — read and execute
  • 4 — read only
  • 3 — write and execute
  • 2 — write only
  • 1 — execute only
  • 0 — no permission

The final argument is a callback which is called when the file permission change operation ends. It has an err parameter which is null when the operation succeeds and has an object with the error information when the operation fails.

We can use the fchmod function to change the file permission asynchronously like in the following code:

const fsPromises = require("fs").promises;  
const fs = require("fs");(async () => {  
  try {  
    const { fd } = await fsPromises.open("./files/file.txt", "r");  
    fs.fchmod(fd, 0o777, err => {  
      if (err) throw err;  
      console.log("File permission change succcessful");  
    });  
  } catch (error) {  
    console.log(error);  
  }  
})();

To confirm that the fchmod function worked as we expected, we can do the run the following commands in Linux systems. One way is to run ls -l , like in the command below:

ls -l ./files/file.txt

When we run that, we get some output like:

-rwxrwxrwx 1 hauyeung hauyeung 16 Nov  2 12:26 ./files/file.txt

The leftmost column has the permissions. rwxrwxrwx is the same as 777 which means that the fchmod function call worked correctly.

We can also use the stat command.

File: './files/file.txt'  
  Size: 16              Blocks: 0          IO Block: 512    regular file  
Device: eh/14d  Inode: 22799473115106242  Links: 1  
Access: (0777/-rwxrwxrwx)  Uid: ( 1000/hauyeung)   Gid: ( 1000/hauyeung)  
Access: 2019-11-02 12:26:47.996290200 -0700  
Modify: 2019-11-02 12:26:47.996290200 -0700  
Change: 2019-11-03 10:34:58.342785500 -0800  
 Birth: -

We can see from the Access field that we have 0777 as the permission, which means that the fchmod operation worked correctly.

There’s also a synchronous version of the fchmod function called the fchmodSync function. The function takes 2 arguments. The first is the file descriptor, which is an integer. The second is the mode, which is the permission and sticky bits we want to change for the file. The mode should be the same as the ones we have above. It returns undefined .

We can use it along with the openSync function, which is the synchronous version of the open function to get the file descriptor, and then use the file descriptor returned from the openSync function as an argument for the fchmodSync function, like in the following code:

const fs = require("fs");try {  
  const fd = fs.openSync("./files/file.txt", "r");  
  fs.fchmodSync(fd, 0o777);  
  console.log("File permission change succcessful");  
} catch (error) {  
  console.log(error);  
}

The openSync function takes the same arguments as the open function but without the callback function as the third argument. When we run the code above, we should get the same output as the asynchronous version that we have previously.

Changing File Ownership of File Referenced by the File Descriptor with the fs.fchown Function

The fchown function let us change the ownership of a file asynchronously. It takes 4 arguments. The first argument is the file descriptor which is an integer. The second argument is the UID, which is the user ID of the user. The third argument is the GID, which is the group ID, and the fourth argument is a callback function which has an err parameter which is null is the chown operation is successful, otherwise err will be an error object. The callback function is called when the chown operation is finished whether it’s successful or not.

To use the fchown function, we can write something like the following code:

const fsPromises = require("fs").promises;  
const fs = require("fs");(async () => {  
  try {  
    const { fd } = await fsPromises.open("./files/file.txt", "r");  
    fs.fchown(fd, 1000, 1000, err => {  
      if (err) throw err;  
      console.log("File permission change succcessful");  
    });  
  } catch (error) {  
    console.log(error);  
  }  
})();

with UID 1000 and group with GID 1000. To find out the UID and GID of the user and group of the current user in Linux systems, we can use the id command.

The fchown function has a synchronous version called fchownSync . It takes 3 arguments. The first argument is the file descriptor which is an integer. The second argument is the UID, which is the user ID of the user. The third argument is the GID, which is the group ID. It returns undefined . We can use it along with the openSync function to get the file descriptor like in the following code:

const fs = require("fs");try {  
  const fd = fs.openSync("./files/file.txt", "r");  
  fs.fchownSync(fd, 1000, 1000);  
  console.log("File permission change succcessful");  
} catch (error) {  
  console.log(error);  
}

To confirm that the fchown or fchownSync worked as we expected, we can do the run the following commands in Linux systems. One way is to run ls -l , like in the command below:

ls -l ./files/file.txt

We can also use the stat command. For example, we can run stat ./files/file.txt to get the ownership information of ./files/file.txt from the Uid and Gid output. With the stat command, we should get output that looks something like:

File: './files/file.txt'  
  Size: 16              Blocks: 0          IO Block: 512    regular file  
Device: eh/14d  Inode: 22799473115106242  Links: 1  
Access: (0555/-r-xr-xr-x)  Uid: ( 1000/hauyeung)   Gid: ( 1000/hauyeung)  
Access: 2019-11-02 12:26:47.996290200 -0700  
Modify: 2019-11-02 12:26:47.996290200 -0700  
Change: 2019-11-02 12:44:45.037581600 -0700  
 Birth: -

Also, we can filter out the information we don’t need and just get the UID and GID with the "%U %G" format sequence. For example, we can run stat -c “%U %G” ./files/file.txt to get the UID and GID only for file.txt .

With the fchown function, we can change the ownership of a file given the file descriptor of the file. There are 2 asynchronous versions of the function, one is the regular asynchronous version and one is the promise version. There’s also one synchronous version of the chown function, which is the chownSync function. They both change the owner by passing in the UID and GID of the user of your choice. UID is the user ID and GID is the group ID. We can change the permission of the file identified with the file descriptor with the fchown function. . There are 2 asynchronous versions of the fchown function, one is the regular asynchronous version and one is the promise version. There’s also a synchronous version of the fchown function called the fchownSync function. We can confirm that the operations worked with the ls -l or stat commands on POSIX systems.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *