22

I have a program where I need to set the permissions of a file (say /home/hello.t) using chmod and I have to read the permissions to be set from a file. For this I first read the permissions into a character array and then try to modify the permissions of the file. But I see that permissions are set in a weird manner.

A sample program I have written:

main()
{
    char mode[4]="0777";
    char buf[100]="/home/hello.t";
    int i;
    i = atoi(mode);
    if (chmod (buf,i) < 0)
        printf("error in chmod");
}

I see that the permissions of the file are not set to 777. Can you please help me out on how to set the permissions of the file after reading the same from a character array.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Raghav
  • 229
  • 1
  • 2
  • 3

1 Answers1

44

The atoi() function only translates decimal, not octal.

For octal conversion, use strtol() (or, as Chris Jester-Young points out, strtoul() - though the valid sizes of file permission modes for Unix all fit within 16 bits, and so will never produce a negative long anyway) with either 0 or 8 as the base. Actually, in this context, specifying 8 is best. It allows people to write 777 and get the correct octal value. With a base of 0 specified, the string 777 is decimal (again).


Additionally:

  • Do not use 'implicit int' return type for main(); be explicit as required by C99 and use int main(void) or int main(int argc, char **argv).
  • Do not play with chopping trailing nulls off your string.

    char mode[4] = "0777";
    

    This prevents C from storing a terminal null - bad! Use:

    char mode[] = "0777";
    

    This allocates the 5 bytes needed to store the string with a null terminator.

  • Report errors on stderr, not stdout.

  • Report errors with a newline at the end.
  • It is good practice to include the program name and file name in the error message, and also (as CJY pointed out) to include the system error number and the corresponding string in the output. That requires the <string.h> header (for strerror()) and <errno.h> for errno. Additionally, the exit status of the program should indicate failure when the chmod() operation fails.

Putting all the changes together yields:

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

int main(int argc, char **argv)
{
    char mode[] = "0777";
    char buf[100] = "/home/hello.t";
    int i;
    i = strtol(mode, 0, 8);
    if (chmod (buf,i) < 0)
    {
        fprintf(stderr, "%s: error in chmod(%s, %s) - %d (%s)\n",
                argv[0], buf, mode, errno, strerror(errno));
        exit(1);
    }
    return(0);
}

Be careful with errno; it can change when functions are called. It is safe enough here, but in many scenarios, it is a good idea to capture errno into a local variable and use the local variable in printing operations, etc.

Note too that the code does no error checking on the result of strtol(). In this context, it is safe enough; if the user supplied the value, it would be a bad idea to trust them to get it right.


One last comment: generally, you should not use 777 permission on files (or directories). For files, it means that you don't mind who gets to modify your executable program, or how. This is usually not the case; you do care (or should care) who modifies your programs. Generally, don't make data files executable at all; when files are executable, do not give public write access and look askance at group write access. For directories, public write permission means you do not mind who removes any of the files in the directory (or adds files). Again, occasionally, this may be the correct permission setting to use, but it is very seldom correct. (For directories, it is usually a good idea to use the 'sticky bit' too: 1777 permission is what is typically used on /tmp, for example - but not on MacOS X.)

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 1
    `strtoul` is even better, because who uses negative modes, seriously? :-P Also, report errors with `errno` included, such as by using `perror` or `strerror` (or `%m` in the format string, if using GNU libc). – C. K. Young Dec 31 '10 at 07:11
  • Thanks, it works great. And one more thanks for correcting my programming errors. – Raghav Dec 31 '10 at 08:52
  • The numeric errno value is conventionally not shown in error messages, as it is redundant and may disclose unwanted information about the OS type. – jilles Jan 01 '11 at 13:18
  • @jilles: At one level you are correct - but most of the Unix error messages are distinctive enough to identify the O/S (as Unix), and omitting information like error number is less helpful than including it. Most of the time, I'm working with users that type the command on the machine; hiding the o/s from the user is immaterial. In a web environment, what is reported to the user is a different story, but what goes in the logs should include the system error number (and maybe the text), at least in my estimation. Similar comments apply in a GUI program - you handle errors differently then. – Jonathan Leffler Jan 01 '11 at 15:32
  • May want to set errno to 0 prior to the chmod call. – Raffi Khatchadourian Jan 16 '13 at 19:32
  • @RaffiKhatchadourian: There's no need to set `errno` before `chmod()`. The code only checks `errno` if `chmod()` reports failure, and `chmod()` is defined to set `errno` on failure, so it is quite safe. Sometimes, you do need to set `errno`. For example, you might need to set it before the call to `strtol()` because it only sets `errno` if there are some classes of error, but not for other errors (and no C library function — or POSIX function, AFAIK — set `errno` to 0). However, in this case, if the value from `strtol()` is problematic, you get GIGO results. – Jonathan Leffler Jan 16 '13 at 19:50
  • @JonathanLeffler Ah, ok, so setting errono to 0 prior to the call is more of a general comment. – Raffi Khatchadourian Jan 16 '13 at 20:38
  • @RaffiKhatchadourian: for a few (very few) functions, it is necessary to set `errno` to 0 before calling the function if you are going to do thorough error checking; for most, it is not necessary. – Jonathan Leffler Jan 16 '13 at 21:37
  • @JonathanLeffler very few, although `strtol` which you have just used is one of them :) your usage here is fine of course. – robbie_c Jan 28 '13 at 17:01
  • what an amazing answer – Luke De Feo Dec 09 '14 at 14:29