Contemporary operating systems permit more than one person to use a computer by providing a separate account for each person. The operating system protects the files associated with each account and allows or disallows other users to view or modify files that belong to other accounts. POSIX-compliant systems (like Unix, Linux, and macOS) define three kinds of file access: by the file owner (called the user), by a group, and by others (everyone else). A group consists of one or more accounts and has an intermediate level of access, which can allow more access than others while at the same time allowing less access than the user. Windows maintains this information in an Access Control List or ACL, but some file information is still available using the following technique. POSIX systems compress or encode file type and access information into a 16-bit integer to save disk space:
7-bits for the mode: one of the seven bits distinguishes between a regular file and a directory (1 for a directory and 0 for a regular file). The other six bits indicate other file attributes that we ignore for simplicity.
9-bits for file access control: controlled by three sets of three bits; in each set, 1 grants permission, and 0 disallows it. The three sets are named:
User
Group
Others
Binary
Decimal
Hexadecimal
Read
100
4
0x4
Write
010
2
0x2
Execute
001
1
0x1
Binary
Decimal
Permissions
000
0
none
001
1
execute
010
2
write
011
3
write + execute
100
4
read
101
5
read + execute
110
6
read + write
111
7
read + write + execute
(a)
(b)
File access control encoded with bits.
File permissions. For each kind of owner, the operating system encodes file permissions as a 3-bit pattern.
File access permission combinations. With three bits, file systems can represent eight unique permission combinations, including no access.
Problem
Our task is to write a program that uncompresses or decodes the file type and the nine bits representing the read, write, and execute permissions for the user, group members, and others. Although there are many kinds of files, our solution must only distinguish between directories and "regular" files. For now, our program only needs to print the permissions as a decimal value whose meaning matches Figure 1(a).
Program Solution
For now, we must be content with just a few program fragments. We need to study structures and control statements before we can understand a complete version of the program. Until then, we focus on uncompressing the tightly encoded file access information. We begin with the encoded data already stored in the unsigned short integer named st_mode. The first part of the solution uses an If-statement, which we study in the next chapter, but operates as it would in a Java program.
Determining if the file is a regular file or a directory.
The 16-bit unsigned integer st_mode contains information about a file. That information was previously recovered from the computer's file system by a system call not illustrated here.
S_IFREG is a symbolic constant, denoting a regular file, defined in a system header file. It consists of a 16-bit bit-mask with fifteen 0-bits and one 1-bit.
The bitwise-AND between the mask and the value stored in st_mode will produce a non-zero value only if the file described by st_mode is a directory.
Displaying file permissions. The file permissions are displayed as decimal (i.e., base-10) numbers, which we interpret as illustrated in Figure 1.
(st_mode >> n) shifts the bits in st_moden places to the right. Recall that st_mode consists of three 3-bit groups. The left-most three bits encode the user's permissions, the three middle bits encode the group's permissions, and the right-most three bits encode the other's permissions (and are not shifted). The parentheses cause the bit-shift operation to take place first.
(...) & 0x7 "masks out" all but the right-most three bits. 716 = 0...01112, so the bitwise-AND will always yield 0s in the first thirteen bit positions, and will "pass through" the last three bits of st_modes.
Caution
A short integer usually spans two bytes of memory, and the byte order depends on the underlying computer hardware. The solution presented here works on a little endian computer (e.g., Intel or AMD). It does not work on big endian hardware.
Bitwise Operations Illustrated
The concepts and the operators appearing in Figure 2 are likely unfamiliar to most readers, so we attempt to gain some insight by looking at a graphical representation of what occurs when each bit-shift statement runs. Notice that the figures below present the three output statements in the opposite order in which they appear in the program fragment.