Question

I need to make a new folder that has the same permissions as another folder. My end goal for the assignment is to copy a directory and it's contents entirely. This is one of my first steps and I can't seem to get it to work.

What I have is below:

struct stat srcstat;
int srcstatus = stat(source, &srcstat);
if (chdir(dest))
    if (mkdir(dest, srcstat.st_mode)){
        printf("error: could not create <dest>\n");
        exit(1);
    }

source is a cstring with the path to the folder whose permissions I want to use. dest is a c string with the path to the new folder.

the ls with the old and new folders permissions is below

drwxrwxrwx 2 kingacev CS-Majors 4096 Apr 18 17:03 test
drwxr-xr-x 2 kingacev CS-Majors 4096 Apr 18 18:12 test3

my first thought is that I can't use srcstat.st_mode in the way I'm attempting. If that's the case, is there an equally simple way to do what I'm trying to do? If not, how far from the mark am I?

Was it helpful?

Solution

You are running foul of the "umask", a per-process setting which masks out permission bits in file and directory creation operations.

There is no safe way to disable the umask. What you should do instead is create the directory with mode zero (i.e. all access denied) and then use chmod (the system call, not the shell command of the same name) to adjust the permissions to what you want.

Your program fragment has many other bugs. This is tricky to get right, and might well be a security hole if you get it wrong, so I shall write out the correct code in detail:

int
make_directory_like(const char *to_create,
                    const char *prototype)        
{
    struct stat st;
    if (lstat(prototype, &st)) {
        fprintf(stderr, "lstat: %s: %s\n", prototype, strerror(errno));
        return -1;
    }
    if (!S_ISDIR(st.st_mode)) {
        fprintf(stderr, "lstat: %s: %s\n", prototype, strerror(ENOTDIR));
        return -1;
    }
    /* create directory initially with no perms at all - this is the only
       safe way to suppress the effects of the umask.  */
    if (mkdir(to_create, 0000)) {
        if (errno != EEXIST) {
            fprintf(stderr, "mkdir: %s: %s\n", to_create, strerror(errno));
            return -1;
        } else {
            /* check whether the thing that exists is a directory */
            struct stat st2;
            if (lstat(to_create, &st2)) {
                fprintf(stderr, "lstat: %s: %s\n", to_create, strerror(errno));
                return -1;
            }
            if (!S_ISDIR(st2.st_mode)) {
                fprintf(stderr, "mkdir: %s: %s\n", to_create, strerror(EEXIST));
                return -1;
            }
        }
    }
    if (chmod(to_create, st.st_mode & ~S_IFMT)) {
        fprintf(stderr, "chmod: %s: %s\n", to_create, strerror(errno));
        return -1;
    }
    return 0;
}

Exercises for you:

  1. Why is this the only safe way to suppress the effects of the umask? (Hint: threads. But that's only one of several reasons.)
  2. Why do I use lstat rather than stat?
  3. Why is it necessary to stat the to-create path if mkdir fails with errno == EEXIST?
  4. Why is it incorrect to use chdir as you were doing? (There are two reasons.)
  5. Why is it safe to go ahead and call mkdir on the to-create path when we don't know whether there is already something there?
  6. Why is that & ~S_IFMT thing necessary?

OTHER TIPS

You'll want to set the umask to zero. See http://man7.org/linux/man-pages/man2/umask.2.html

Otherwise, some bits (often 022, like you're seeing) will always be cleared.

umask(0);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top