Hello, and welcome back to CS631 "Advanced Programming in the UNIX Environment". This is week 3, segment 3, where we're looking at how the permissions defined in the struct stat st_mode are applied. Remember from our previous segment that a process has both an effective as well as a real UID and an effective as well as real GID associated with it, and that we said that it is the effective UID that is used to determine access. --- Let's take a closer look at the order in which permission checks are done. The permissions of a file are encoded in the struct stat's st_mode, and as you know, they are broken down into three groups: 'owner' (or 'user'), 'group', and 'other'. So in order to open a file, you need... - ...first of all execute permissions on the directory the file is in. This is because in order to open a file you need to find the mapping of the filename to inode, which we said earlier is stored in a file of type "directory". Performing this lookup requires "execute" permissions on the directory -- "read" permissions of a directory are required only to list the contents of the directory, to iterate over each entry. Note also that you need execute permissions on all directories in the path specified when opening the file. This makes sense, since to open a full pathname -- say, /var/tmp/dir -- you need to first open /var, and hence require execute permissions on /, then "tmp" under /var (and so require execute permissions on var), and so on. Verify that this holds by creating a directory containing multiple deeper subdirectories; change into that directory and create a file, then remove execute permissions from your parent directory. Can you still open the file you created? Try both a relative and an absolute pathname. Anyway, so you need to be able to execute the directory the file is in to open a file. - Next, to open a file in read or read-write mode, you require read permissions, and - to open a file in write or read-write mode, you need write permissions. Seems fair. - To use O_TRUNC, you need write permissions, since truncating a file modifies it. - To create a new file, you need write and execute permissions on _the directory_ in which you're creating the file. This should make sense, since creating a file requires you to add an entry in the directory, to write into this directory. - To delete a file, the same holds true, and this is perhaps somewhat counter-intuitive: The permissions on the file itself do not matter. This is because deleting a file does not really change the file -- it changes the directory the file is in. All you're doing when deleting a file is editing the directory and removing the link inside the directory. This is why overly permissive modes such as 777 on a directory are so disastrous: any user can remove any file in such a directory and then re-create it, regardless of the permissions on the file itself. We'll get back to this in our next lecture on filesystems, but it's a good idea to confirm this for yourself by creating a directory like that and adding and removing files as different users. - Finally, to execute a file, you need execute permissions, but _not_ read permissions. You can verify this for your compiler executables: change permissions to remove read access to all users, then try to run the program. Note, though, that for programs that are written in an interpreted language, such as shell scripts or python programs, you _do_ need read permissions, because executing those program actually means you are executing the interpreter binary and then feed the file as input to the interpreter. Give that a try as well! --- Knowing which permissions we need, we can now look at the order of consideration of the euid and egid, which will influence which permissions set -- user, group, or other -- are going to take effect: Note that this decision tree here is _ordered_. That is, the first condition that applies decides. They are: - if your process has euid 0, then access is granted. Note that this is irrespective of the permissions on the file. They are simply not considered -- if you are root, you get to do whatever you want. - Next, if the effective UID matches the st_uid on the file in question, then the system looks at the user permission bits. If those allow access, then you're good; if not, you lose. Of interest here is that this ends the decision making process: your group membership or the permissions for 'others' are simply not considered. - If you are not root, and your euid does not match the st_uid, then we go on and look at the effective group id. If that matches the st_gid, and the group permissions in question are set, you get access; if not, you lose. - Finally, if you are neither root, nor your euid matches the st_uid field, _nor_ your egid matches st_gid, then - and only then - do we look at the 'other' permissions and grant or deny access based on those. This first-match decision making can lead to some possibly unexpected results for people not used to Unix permissions and ownerships, so let's show a few examples. --- First, let's note our UID and groups. Next, we create a file and remove any and all permissions from it, like so. To illustrate that root can do whatever they want, let's become the super user. As we can see here, even though file permissions prohibit any read access from anybody, root can still access and read the file. The owner of the file, however, cannot! This is because the owner permission bits are not set. Now let's switch over to user 'fred'. User 'fred' is in the group 'users', and of course cannot display the file that has no permissions set at all. Let's change the group permissions on 'file'. User 'jschauma' is in the group "wheel"; 'file' is owned by the group "wheel", and group permissions allow reading... ...but user "jschauma" still cannot display the file. This is because "jschauma" is the owner, and as explained before, once the UID check is met, the system doesn't care about the group permissions. user permissions deny access to any user with euid == st_uid. Let's change the group ownership to "users". Now for "jschauma" this doesn't make a difference, but for user "fred", it does: because "fred" is in the group "users", and group permissions allow read access, _and_ "fred" is not the owner of the file, "fred" is granted access. Ok, next, let's revoke group access again, but allow "others" to read the file. Now Fred can't read the file again, since the match on the group id prevents the system from considering the "other" permissions. Let's change the group ownership back to "wheel", but leave permissions as they are. And here we go: Fred can read the file again, because Fred is not in the group "wheel", and so this time the "other" permissions are the ones relevant for Fred's access. Of course root can still access the file, no surprise. Finally, notice that trying to remove a file where the owner does not have write access yields a prompt here. This is merely a nicety of the "rm(1)" command, however, a way to ask "are you sure?"; the system call to unlink the file does not require a confirmation. --- Ok, time for another break. We've run through a number of examples that illustrate the order of checks performed by the system to determine whether or not to grant access to a file. Make sure to go back and run these examples yourself - just watching these videos is not going to be as helpful to your understanding as running them yourself. When you do, play around with the permissions and ownerships, change things around, and make sure the outcomes are what you expected. In our next segment, we'll look at the syscalls to to make the various chmod(1) and chown(1) changes we've seen. In fact, after the next video segment, you should be able to implement those commands yourself, for the most part. Hope you stick around, and thanks for watching!