File creation permissions in C - c

I'm creating a file in C using the following code:
int outfd = open(arg,O_CREAT|O_TRUNC|O_WRONLY, f_per);
f_per being the file permission numbers.
Setting f_per to 0644, executing the code and doing an ls -l gives me the (output) file permissions set as -rw-r--r-- which is expected. However, setting things to 0777 gives permissions as -rwxrwxr-x instead of -rwxrwxrwx. Any idea why this happens?

As per the POSIX page for the open call, under O_CREAT:
... the access permission bits of the file mode shall be set to the value of the argument following the oflag argument taken as type mode_t modified as follows: a bitwise AND is performed on the file-mode bits and the corresponding bits in the complement of the process' file mode creation mask.
The mode creation mask (or umask) can be considered a subtractive one. For example, if you attempt to create a file with permissions rwxrwxrwx/0777 when your file mode creation mask is -------w-/0002, you'll actually end up with:
rwxrwxrwx
& rwxrwxr-x (complement of -------w-)
=========
rwxrwxr-x
This appears to be the situation you are encountering.
If you want to actually create a file of the specific permissions, you can temporarily disable the umask by setting it to zero (and restoring it afterwards), something like:
mode_t oldmask = umask(0); // needs sys/stat.h
int outfd = open(arg, O_CREAT|O_TRUNC|O_WRONLY, 0777);
umask(oldmask);

Related

c Open() function

I have some trouble understanding the arguments in the open function, specifically used in the context of creating an output file. I do not quite understand the roles of flags and file permissions (the 2nd and 3rd arguments in the function). For instance, if I have the file permission 00200 (user has write permission), and the flag O_RDONLY (Read only), then can I read the file or write the file?
The signature of open is as follows:
int open(const char *pathname, int flags, mode_t mode);
There are three sets of "permissions" at play: The permissions of the file itself, the flags, and the mode.
The permissions of the file itself (e.g. 00200 meaning only user can write) specify what the operating system allows a program to do.
When you specify the flags, you indicate what you want to do with the file. For example, if the file is readonly to you (e.g. rwxr-xr-x and you're not the owner), you will be allowed to open the file with O_RDONLY. If you attempt to open the file with O_RDWR or O_WRONLY, you will receive an EPERM (operation not permitted) error in errno.
The mode parameter is only relevant when you create a new file, such as when you open a file that doesn't exist1 and the flag O_CREAT is specified. The file is created on the filesystem and its permissions are given by mode & ~umask (see man 2 umask for more details).
1 Of course, the containing directory must exist and you must have write+exec permissions on that directory.

open() function parameters

If you look at this code block below by taking into consideration the last parameter "0", Does write line work properly ?
filename = argv[1];
string = "Example string";
if (stat(argv[1], &buf) != 0)
{
fd = open(filename, O_WRONLY | O_CREAT, 0);
if (fd < 0)
{
perror(filename);
exit(1);
}
write(fd, string, strlen(string));
close(fd);
}
else
{
print("%s file exists\n", filename);
}
From the manpage:
mode specifies the permissions to use in case a new file is created. This argument must be supplied when O_CREAT is specified in flags; if O_CREAT is not specified, then mode is ignored. The effective permissions are modified by the process's umask in the usual way: The permissions of the created file are (mode & ~umask). Note that this mode applies only to future accesses of the newly created file; the open() call that creates a read-only file may well return a read/write file descriptor.
The following symbolic constants are provided for mode:
S_IRWXU 00700 user (file owner) has read, write and execute permission
S_IRUSR 00400 user has read permission
S_IWUSR 00200 user has write permission
S_IXUSR 00100 user has execute permission
S_IRWXG 00070 group has read, write and execute permission
S_IRGRP 00040 group has read permission
S_IWGRP 00020 group has write permission
S_IXGRP 00010 group has execute permission
S_IRWXO 00007 others have read, write and execute permission
S_IROTH 00004 others have read permission
S_IWOTH 00002 others have write permission
S_IXOTH 00001 others have execute permission
So, specifying a mode of zero, you will create a file with the permissions of 0 & ~umask, i.e. a file without any permissions.
What exactly the filesystem makes of this is not in the domain of the open() or write() functions.
It is valid,
This is from open(2) Linux manual pages
The mode argument specifies the file mode bits be applied when a new file is created. This argument must be supplied when O_CREAT or O_TMPFILE is specified in flags; if neither O_CREAT nor O_TMPFILE is specified, then mode is ignored. The effective mode is modified by the process's umask in the usual way: in the absence of a default ACL, the mode of the created file is (mode & ~umask). Note that this mode applies only to future accesses of the newly created file; the open() call that creates a read-only file may well return a read/write file descriptor.
In theory then, your access to the file will be valid until you call close() as I understand the part I highlighted in the above excerpt.
Interesting question. POSIX says:
The argument following the oflag argument does not affect whether the file is open for reading, writing, or for both.
Which means that since you're handling the error return from open, if you reach the write line the behavior is well defined.
To expand a bit why this works. On most filesystems on unix-like systems, the meta-data related to a file should not affect already open file descriptors. You can for example remove a file that you have opened. This is in fact done quite commonly with temporary files, so that you don't need to remember to delete them on exit. The same applies to permissions or even ownership of the file. In fact, you can chroot while holding a file open and you can still write to it without actually being able to see it. You can even use file descriptor passing to give an open file descriptor to another process that wouldn't be allowed to open that file. This is quite commonly used for privilege separation. The permissions you had when creating a file descriptor are valid regardless of the changes to permissions later. So your question is a very interesting edge case because it asks if the filesystem permissions of the file are set before or after we create a file descriptor for it and POSIX seems to be clear on that.
I can only think of two exceptions to that right now. First is when someone forcibly remounts a filesystem to read-only in that case the kernel will go through horrifying gymnastics to invalidate your file descriptor which will make all its operations fail. Second one is AFS where your permissions are actually checked when you close the file (or, when the last user of the file on your local system closes it which sends it to the server), which leads to hilarious problems where your time-limited access tokens were valid when you opened a file but aren't valid any longer when you close it. This is also why close returns errors (but that's another rant).
This is why I mentioned error handling above. Even though POSIX says that it should not have an effect, I could see AFS or certain other file systems refusing to open such a file.

Unix Executable file get changed to Document while transferring through sockets

I am on Mac OSX. I have written two programs to transfer a "Unix Executable file" from one place to another(server program to client program). Here is the code:
Server Side code:
fileDescriptor = open(reqMsg.filename, O_RDONLY);
if (fileDescriptor > 0)
while ((msgLength = read(fileDescriptor, content, MESSAGESIZE)) > 0)
send(data.remoteSocketDes, content, msgLength, 0);
Client Side code:
fileDescriptor = open(data.filename, O_CREAT|O_WRONLY);
if (fileDescriptor > 0)
while ((msgLength = recv(localSocketDes, content, MESSAGESIZE, 0)) > 0)
write(fileDescriptor, content, msgLength);
The size and content of the file is exactly same but the type of file is changed from "Unix Executable file" to "Document". Eventually I am not able to execute it from the place where it has been copied.
Note: the code working properly fine for ".txt" files.
The "executable" property of a file is a function of its permissions, not its content. Simply copying its contents into another file will not preserve this property.
If you want to preserve this, you will need to serialize the permissions of the original file (which you can obtain using stat() on the file), send them to the client, and apply them to the new file (using chmod(), or by passing the permissions to open()).
If preserving file metadata such as this is important, you may also want to consider preserving the creation and modification times and extended attributes of the file as well.
Under Unix (and variants like OS X), the determining factor whether you can execute a command from the shell prompt is whether the file you are trying to execute has the execute bit set (and you have permissions to execute it).
To handle this from the command prompt, you would just use chmod +x {file}, but if you know the incoming file is an executable (possibly by testing in sender first and sending that information through the socket in a preamble), you could code your receiving program to set the execute bit if appropriate by using chmod():
chmod( data.filename, permission)
Where you calculate permission by or'ing the execute bits with the default umask, like so:
mode_t mask = umask (0);
umask (mask);
mode_t permission = mask | S_IXUSR | S_IXGRP | S_IXOTH;
Note that this will set the permissions to world, group, and owner writable, which might not be desirable. Also note that you MUST call umask the second time, since the first call is destructive.

Creating a file with open() or creat() has fewer permission bits set than I asked for

I am writing a program to mimic the cp utility. However, I cannot get the file permissions to work correctly. I know that they are stored in the structure stat and stored in the st_mode field with stat.
My issue is that I do not get the write permission for the group or other categories, i.e. I get -rwxr-xr-x as the permissions for the file even though the source file is -rwxrwxrwx. The statement where I set the permissions is below.
if ( (dest_fd = open(dest_file, O_WRONLY|O_CREAT, (stats.st_mode & S_IRUSR)|(stats.st_mode & S_IWUSR)|(stats.st_mode & S_IXUSR)|(stats.st_mode & S_IRGRP)|(stats.st_mode & S_IWGRP)|(stats.st_mode & S_IXGRP)|(stats.st_mode & S_IROTH)|(stats.st_mode & S_IWOTH)| (stats.st_mode & S_IXOTH))) < 0)
{
printf("There was a problem opening the destination file.");
exit(EXIT_FAILURE);
}//ends the if statement opening the destination file.
The answers so far are right that the problem is umask, but rather than clearing the umask (this is dangerous if your program is multi-threaded or if you might be calling any library functions that create files) I would treat the umask as a user configuration variable you are not allowed to modify, and instead call fchmod on the files after creating them to give them the final permissions you want. This may be necessary anyway to give certain permissions like suid/sgid, which some kernels remove whenever the file is modified. I would also initially create the file with mode 0600, so that there's no race condition between opening it and changing permissions during which another user could get an open handle on the file.
The cause of the problem is
The permissions of the created file are (mode & ~umask)
Typically, umask is 022, so that prohibits creating world-writable files.
*nix masks out mode bits in files you create, but you can change the mask using the umask() function. man umask (perhaps man 2 umask) for details.
You can use the chmod(2) syscall to change the permissions of an existing file or directory or fchmod(2) to set the permissions given an open file descriptor.
To be more secure and to prevent exploitation of possible race conditions, you can use a very restrictive set of permissions while creating the file and then use chmod(2) to restore the original permissions. This is what cp -a does (except that it creates the file with the default permissions):
$ strace cp -a file file1
...
open("file1", O_WRONLY|O_TRUNC) = 4
...
fchmod(4, 0100640) = 0
...
chmod(2) and fchmod(2) are not affected by the value of the umask.

Using the open() system call

I'm writing a program that writes output to a file. If this file doesn't exist, I want to create it.
Currently, I'm using the following flags when calling open:
O_WRONLY | O_CREATE
However, when this creates the file, it doesn't give me any permissions to write to it...
How can I use open so that it creates a file if it doesn't exist, but will create it with necessary permissions when needed?
Thanks!
You probably need the third argument. For example:
open('path',O_WRONLY|O_CREAT,0640);
Just use the optional third argument to open:
int open(const char* pathname, int flags, mode_t mode);
so like this:
open("blahblah", O_CREAT | O_WRONLY, S_IRUSR | S_IWUSER | S_IRGRP | S_IROTH);
See man open(2).
On Linux there's a third argument you can use to pass permissions. S_IWUSR should be the flag to give you write permissions, but in practice you'll probably want to use more flags than just that one (bitwise or'd together). Check the manpage for a list of the permission flags.
From the manual:
O_CREAT
If the file exists, this flag has no effect except as noted under O_EXCL
below. Otherwise, the file shall be
created; the user ID of the file shall
be set to the effective user ID of the
process; the group ID of the file
shall be set to the group ID of the
file's parent directory or to the
effective group ID of the process; and
the access permission bits (see
) of the file mode shall
be set to the value of the third
argument taken as type mode_t modified
as follows: a bitwise AND is performed
on the file-mode bits and the
corresponding bits in the complement
of the process' file mode creation
mask. Thus, all bits in the file mode
whose corresponding bit in the file
mode creation mask is set are cleared.
When bits other than the file
permission bits are set, the effect is
unspecified. The third argument does
not affect whether the file is open
for reading, writing, or for both.
Implementations shall provide a way to
initialize the file's group ID to the
group ID of the parent directory.
Implementations may, but need not,
provide an implementation-defined way
to initialize the file's group ID to
the effective group ID of the calling
process.
So it seems you need to pass a third argument specifying the desired file permissions.
Note that under POSIX (Unix, Linux, MacOS, etc) you can open and create a file with any permissions you choose, including 0 (no permission for anyone), and yet still write to the file if opened for writing.

Resources