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:
- Why is this the only safe way to suppress the effects of the umask? (Hint: threads. But that's only one of several reasons.)
- Why do I use
lstat
rather thanstat
? - Why is it necessary to stat the to-create path if
mkdir
fails witherrno == EEXIST
? - Why is it incorrect to use
chdir
as you were doing? (There are two reasons.) - 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? - Why is that
& ~S_IFMT
thing necessary?