Changing a File’s Permissions

In Unix-like operating systems, the chmod shell command alters a file’s permissions. From the C library, the chmod() function does the same thing.

The chmod() function is prototyped in the sys/stat.h header file, along with all those delicious defined constants and macros mentioned in last week’s Lesson. Here’s the man (page 2) format:

int chmod(const char *path, mode_t mode);

The path is the filename string or a full pathname to the file. It can be a regular file, directory, device, whatever. The mode argument is the same mode_t type found as the st_mode member of the stat structure filled by the stat() function.

My advice would be to not randomly set various bits for a file’s permission (or mode) without knowing the current permissions. Therefore, I think the best approach to changing a file’s permissions is to first use the stat() function to examine the permissions, then use chmod() to and reset specific permissions. The code below follows this approach, setting the group write and other write permissions:

2020_05_09-Lesson.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>

void output_permissions(mode_t m)
{
    putchar( m & S_IRUSR ? 'r' : '-' );
    putchar( m & S_IWUSR ? 'w' : '-' );
    putchar( m & S_IXUSR ? 'x' : '-' );
    putchar( m & S_IRGRP ? 'r' : '-' );
    putchar( m & S_IWGRP ? 'w' : '-' );
    putchar( m & S_IXGRP ? 'x' : '-' );
    putchar( m & S_IROTH ? 'r' : '-' );
    putchar( m & S_IWOTH ? 'w' : '-' );
    putchar( m & S_IXOTH ? 'x' : '-' );
    putchar('\n');
}

int main(int argc, char *argv[])
{
    const char *filename;
    struct stat fs;
    int r;

    if( argc<2 )
    {
        puts("Filename required");
        exit(1);
    }

    filename = argv[1];
    printf("Permissions for '%s':\n",filename);
    r = stat(filename,&fs);
    if( r==-1)
    {
        fprintf(stderr,"Error reading '%s'\n",filename);
        exit(1);
    }

    /* output the current permissions */
    puts("Current permissions:");
    output_permissions(fs.st_mode);

    puts("Set group and other write permissions");
    r = chmod( filename, fs.st_mode | S_IWGRP+S_IWOTH );
    if( r!=0)
    {
        fprintf(stderr,"Unable to reset permissions on '%s'\n",filename);
        exit(1);
    }

    puts("Updated permissions:");
    stat(filename,&fs);
    output_permissions(fs.st_mode);

    return(0);
}

The output_permissions() function runs through the file’s st_mode value, passed in variable m. Each of the nine permissions is evaluated in a putchar() function, the ternary operator determines whether the given bit is set or reset.

In the main() function, the stat() function at Line 33 grabs the named file’s current permissions. This value is output at Line 42 with a call to the output_permissions() function.

At Line 45, the chmod() function performs a logical OR operation with the file’s current permissions (fs.st_mode): the S_IWGRP and S_IWOTH defined constants set new permissions for the file.

After the permissions are changed, a second call to the stat() function is made at Line 53. The updates permissions are output. Here’s a sample run:

Permissions for 'gettysburg.txt':
Current permissions:
rw-r--r--
Set group and other write permissions
Updated permissions:
rw-rw-rw-

The gettysburg.txt file originally had permissions rw-r--r--: owner read/write, all others read-only. The chmod() function added write permissions for group and other users, rendering the updated permissions as rw-rw-rw.

On my system, I reset the permissions back their original settings after running the program. Typical permissions for a file you own on are rw-r--r--. The shell chmod command used to reset permissions is chmod 644. This command uses octal values to represent the three groups: owner, group (or wheel), and other.

All this information ties back into an earlier blog post on creating a file “in the raw.” I offer the specifics in next week’s Lesson.

Leave a Reply