11.3. Querying and Changing Inode Information 11.3.1. Finding Inode Information The beginning of this chapter introduced an inode as the data structure that tracks information about a file rather than just a single process's view of it. For example, a file's size is a constant at any given time it does not change for different processes that have access to the file (compare this with the current position in the file, which is unique for the result of each open() rather than a property of the file itself). Linux provides three ways of reading a file's inode information: #include <sys/stat.h> int stat(const char * pathname, struct stat * statbuf); int lstat(const char *pathname, struct stat * statbuf); int fstat(int fd, struct stat * statbuf); The first version, stat(), returns the inode information for the file referenced by pathname, following any symlinks that are present. If you do not want to follow symlinks (to check if a file name is a symlink, for example), use lstat() instead, which does not follow them. The final version, fstat(), returns the inode referred to by an open file descriptor. All three system calls fill in the struct stat referenced by statbuf with information from the file's inode. Table 11.3 describes the information available from struct stat. Table 11.3. Members of struct statType | Field | Description |
---|
dev_t | st_dev | The device number the file resides on. | ino_t | st_ino | The file's on-disk inode number. Each file has an on-disk inode number unique for the device it is on. Thus the (st_dev, st_ino) pair provides a unique identification of the file. | mode_t | st_mode | The mode of the file. This includes information on both the file permissions and the type of file. | nlink_t | st_nlink | The number of pathnames that reference this inode. This does not include symlinks, because symlinks reference other file names, not inodes. | uid_t | st_uid | The user ID that owns the file. | gid_t | st_gid | The group ID that owns the file. | dev_t | st_rdev | If the file is a character or block device, this gives the major and minor numbers of the file. See the discussion on mknod() on page 189 for more information on this member and the macros that manipulate its value. | off_t | st_size | The size of the file in bytes. This is defined only for regular files. | unsigned long | st_blksize | The block size for the file system storing the file. | unsigned long | st_blocks | The number of blocks allocated to the file. Normally, st_blksize * st_blocks is a little more than the st_size because some of the space in the final block is unused. However, for files with holes, st_blksize * st_blocks can be substantially smaller than st_size. | time_t | st_atime | The most recent access time of the file. This is updated whenever the file is opened or its inode is modified. | time_t | st_mtime | The most recent modification time of the file. It is updated whenever the file's data has changed | time_t | st_ctime | The most recent change time of the file or the inode, including owner, group, link count, and so on. |
11.3.2. A Simple Example of stat() Here is a simple program that displays information from lstat() for each file name passed as an argument. It illustrates how to use the values returned by the stat() family of functions. 1: /* statsamp.c */ 2: 3: /* For each file name passed on the command line, we display all of 4: the information lstat() returns on the file. */ 5: 6: #include <errno.h> 7: #include <stdio.h> 8: #include <string.h> 9: #include <sys/stat.h> 10: #include <sys/sysmacros.h> 11: #include <sys/types.h> 12: #include <time.h> 13: #include <unistd.h> 14: 15: #define TIME_STRING_BUF 50 16: 17: /* Make the user pass buf (of minimum length TIME_STRING_BUF) rather 18: than using a static buf local to the function to avoid the use of 19: static local variables and dynamic memory. No error should ever 20: occur so we don't do any error checking. */ 21: char * timeString(time_t t, char * buf) { 22: struct tm * local; 23: 24: local = localtime(&t); 25: strftime(buf, TIME_STRING_BUF, "%c", local); 26: 27: return buf; 28: } 29: 30: /* Display all of the information we get from lstat() on the file 31: named as our sole parameter. */ 32: int statFile(const char * file) { 33: struct stat statbuf; 34: char timeBuf[TIME_STRING_BUF]; 35: 36: if (lstat(file, &statbuf)) { 37: fprintf(stderr, "could not lstat %s: %s\n", file, 38: strerror(errno)); 39: return 1; 40: } 41: 42: printf("Filename : %s\n", file); 43: printf("On device: major %d/minor %d Inode number: %ld\n", 44: major(statbuf.st_dev), minor(statbuf.st_dev), 45: statbuf.st_ino); 46: printf("Size : %-10ld Type: %07o " 47: "Permissions: %05o\n", statbuf.st_size, 48: statbuf.st_mode & S_IFMT, statbuf.st_mode & ~(S_IFMT)); 49: printf("Owner : %d Group: %d" 50: " Number of links: %d\n", 51: statbuf.st_uid, statbuf.st_gid, statbuf.st_nlink); 52: printf("Creation time: %s\n", 53: timeString(statbuf.st_ctime, timeBuf)); 54: printf("Modified time: %s\n", 55: timeString(statbuf.st_mtime, timeBuf)); 56: printf("Access time : %s\n", 57: timeString(statbuf.st_atime, timeBuf)); 58: 59: return 0; 60: } 61: 62: int main(int argc, const char ** argv) { 63: int i; 64: int rc = 0; 65: 66: /* Call statFile() for each file name passed on the 67: command line. */ 68: for (i = 1; i < argc; i++) { 69: /* If statFile() ever fails, rc will end up non-zero. */ 70: rc |= statFile(argv[i]); 71: 72: /* this prints a blank line between entries, but not after 73: the last entry */ 74: if ((argc - i) > 1) printf("\n"); 75: } 76: 77: return rc; 78: } 11.3.3. Easily Determining Access Rights Although the mode of a file provides all the information a program needs to determine whether it is allowed to access a file, testing the set of permissions is tricky and error prone. As the kernel already includes the code to validate access permissions, a simple system call is provided that lets programs determine whether they may access a file in a certain way: #include <unistd.h> int access(const char * pathname, int mode); The mode is a mask that contains one or more of the following values: F_OK | The file exists. This requires execute permissions on all the directories in the path being checked, so it could fail for a file that does exist. | R_OK | The process may read from the file. | W_OK | The process may write to the file. | X_OK | The process may execute the file (or search the directory). | access() returns 0 if the specified access modes are allowed, and an EACCES error otherwise. 11.3.4. Changing a File's Access Permissions A file's access permissions and access permission modifiers are changed by the chmod() system call. #include <sys/stat.h> int chmod(const char * pathname, mode_t mode); int fchmod(int fd, mode_t mode); Although chmod() allows you to specify a path, remember that file permissions are based on the inode, not on the file name. If a file has multiple hard links to it, changing the permissions of one of the file's names changes the file's permissions everywhere it appears in the file system. The mode parameter may be any combination of the access and access modifier bits just discussed, bitwise OR'ed together. As there are normally quite a few of these values specified at a time, it is common for programs to specify the new permission value directly in octal. Only the root user and the owner of a file are allowed to change the access permissions for a file all others who attempt to do so get EPERM. 11.3.5. Changing a File's Owner and Group Just like a file's permissions, a file's owner and group are stored in the file's inode, so all hard links to a file have the same owner and group. The same system call is used to change both the owner and the group of a file. #include <unistd.h> int chown(const char * pathname, uid_t owner, gid_t group); int fchown(int fd, uid_t owner, gid_t group); The owner and group parameters specify the new owner and group for the file. If either is -1, that value is not changed. Only the root user may change the owner of a file. When a file's owner is changed or the file is written to, the setuid bit for that file is always cleared for security reasons. Both the owner of a file and the root user may change the group that owns a file, but the owner must be a member of the group to which he is changing the file. If the file has its group execute bit set, the setgid bit is cleared for security reasons. If the setgid bit is set but the group execute bit is not, the file has mandatory locking enabled and the mode is preserved. 11.3.6. Changing a File's Timestamps A file's owner may change the mtime and atime of a file to any arbitrary value. This makes these timestamps useless as audit trails, but it allows archiving tools like tar and cpio to reset a file's timestamps to the same values they had when the file was archived. The ctime is changed when the mtime and atime are updated, so tar and cpio are unable to restore it. There are two ways of changing these stamps: utime() and utimes(). utime() originated in System V and was adopted by POSIX, whereas utimes() is from BSD. Both functions are equivalent; they differ only in the way the new timestamps are specified. #include <utime.h> int utime(const char * pathname, struct utimbuf * buf); #include <sys/time.h> int utimes(const char * pathname, struct timeval * tvp); The POSIX version, utime(), takes a struct utimbuf, which is defined in <utime.h> as struct utimbuf { time_t actime; time_t modtime; } BSD's utimes() instead passes the new atime and mtime through a struct timeval, which is defined in <sys/time.h>. struct timeval { long tv_sec; long tv_usec; } The tv_sec element holds the new atime; tv_usec contains the new mtime for utimes(). If NULL is passed as the second argument to either function, both timestamps are set to the current time. The new atime and mtime are specified in elapsed seconds since the epoch (just like the value time() returns), as defined in Chapter 18. 11.3.7. Ext3 Extended Attributes The primary file system used on Linux systems is the Third Extended File System,[18] commonly abbreviated as ext3. Although the ext3 file system supports all the traditional features of Unix file systems, such as the meanings of the various bits in each file's mode, it allows for other attributes for each file. Table 11.4 describes the extra attributes currently supported, along with the symbolic name for each attribute. These flags may be set and inspected through the chattr and lsattr programs. [18] So named because it is a journaling version of the Second Extended File System, the successor to the Linux Extended File System, which was designed as a more complete file system than the Minix file system, the only one Linux originally supported. Table 11.4. Extended File AttributesAttribute | Definition |
---|
EXT3_APPEND_FL | If the file is opened for writing, O_APPEND must be specified. | EXT3_IMMUTABLE_FL | The file may not be modified or removed by any user, including root. | EXT3_NODUMP | The file should be ignored by the dump command. | EXT3_SYNC_FL | The file must be updated synchronously, as if O_SYNC had been specified on opening. |
As the ext3 extended attributes are outside the standard file system interface, they cannot be modified through chmod() like the file attributes. Instead, ioctl() is used. Recall that ioctl() is defined as #include <sys/ioctl.h> #include <linux/ext3_fs.h> int ioctl(int fd, int request, void * arg); The file whose attributes are being changed must be open, just as for fchmod(). The request is EXT3_IOC_GETFLAGS to check the current flags for the file and EXT3_IOC_SETFLAGS to set them. In either case, arg must point to an int. If EXT3_IOC_GETFLAGS is used, the long is set to the current value of the program's flags. If EXT3_IOC_SETFLAGS is used, the new value of the file's flags is taken from the int pointed to by arg. The append and immutable flags may be changed only by the root user, as they restrict the operations the root user is able to perform. The other flags may be modified by either the root user or the owner of the file whose flags are being modified. Here is a small program that displays the flags for any files specified on the command line. It works only for files on an ext3 file system,[19] however. The ioctl() fails for files on any other type of file system. [19] It actually works just fine on an ext2 file system as well. The two file systems are very similar (an ext3 file system can even be mounted as an ext2 one), and the programs presented here work on both. In fact, if all of the places 3 appears in the source are changed to 2 the programs still compile and function identically. 1: /* checkflags.c */ 2: 3: /* For each file name passed on the command line, display 4: information on that file's ext3 attributes. */ 5: 6: #include <errno.h> 7: #include <fcntl.h> 8: #include <linux/ext3_fs.h> 9: #include <stdio.h> 10: #include <string.h> 11: #include <sys/ioctl.h> 12: #include <unistd.h> 13: 14: int main(int argc, const char ** argv) { 15: const char ** filename = argv + 1; 16: int fd; 17: int flags; 18: 19: /* Iterate over each file name on the command line. The last 20: pointer in argv[] is NULL, so this while() loop is legal. */ 21: while (*filename) { 22: /* Unlike normal attributes, ext3 attributes can only 23: be queried if we have a file descriptor (a file name 24: isn't sufficient). We don't need write access to query 25: the ext3 attributes, so O_RDONLY is fine. */ 26: fd = open(*filename, O_RDONLY); 27: if (fd < 0) { 28: fprintf(stderr, "cannot open %s: %s\n", *filename, 29: strerror(errno)); 30: return 1; 31: } 32: 33: /* This gets the attributes, and puts them into flags */ 34: if (ioctl(fd, EXT3_IOC_GETFLAGS, &flags)) { 35: fprintf(stderr, "ioctl failed on %s: %s\n", *filename, 36: strerror(errno)); 37: return 1; 38: } 39: 40: printf("%s:", *filename++); 41: 42: /* Check for each attribute, and display a message for each 43: one which is turned on. */ 44: if (flags & EXT3_APPEND_FL) printf(" Append"); 45: if (flags & EXT3_IMMUTABLE_FL) printf(" Immutable"); 46: if (flags & EXT3_SYNC_FL) printf(" Sync"); 47: if (flags & EXT3_NODUMP_FL) printf(" Nodump"); 48: 49: printf("\n"); 50: close(fd); 51: } 52: 53: return 0; 54: }; The following is a similar program that sets the ext3 extended attributes for a given list of files. The first parameter must be a list of which flags should be set. Each flag is represented in the list by a single letter: A for append-only, I for immutable, S for sync, and N for the nodump flag. This program does not modify the current flags for the file; only the flags specified on the command line are set. 1: /* setflags.c */ 2: 3: /* The first parameter to this program is a string consisting of 4: 0 (an empty string is okay) or more of the letters I, A, S, and 5: N. This string specifies which ext3 attributes should be turned 6: on for the files which are specified on the rest of the command 7: line -- the rest of the attributes are turned off. The letters 8: stand for immutable, append-only, sync, and nodump, respectively. 9: 10: For example, the command "setflags IN file1 file2" turns on the 11: immutable and nodump flags for files file1 and file2, but turns 12: off the sync and append-only flags for those files. */ 13: 14: #include <errno.h> 15: #include <fcntl.h> 16: #include <linux/ext3_fs.h> 17: #include <stdio.h> 18: #include <string.h> 19: #include <sys/ioctl.h> 20: #include <unistd.h> 21: 22: int main(int argc, const char ** argv) { 23: const char ** filename = argv + 1; 24: int fd; 25: int flags = 0; 26: 27: /* Make sure the flags to set were specified, along with 28: some file names. Allow a "0" to be set to indicate that all 29: of the flags should be reset. */ 30: if (argc < 3) { 31: fprintf(stderr, "setflags usage: [0][I][A][S][N] " 32: "<filenames>\n"); 33: return 1; 34: } 35: 36: /* each letter represents a flag; set the flags which are 37: specified */ 38: if (strchr(argv[1], 'I')) flags |= EXT3_IMMUTABLE_FL; 39: if (strchr(argv[1], 'A')) flags |= EXT3_APPEND_FL; 40: if (strchr(argv[1], 'S')) flags |= EXT3_SYNC_FL; 41: if (strchr(argv[1], 'N')) flags |= EXT3_NODUMP_FL; 42: 43: /* iterate over all of the file names in argv[] */ 44: while (*(++filename)) { 45: /* Unlike normal attributes, ext3 attributes can only 46: be set if we have a file descriptor (a file name 47: isn't sufficient). We don't need write access to set 48: the ext3 attributes, so O_RDONLY is fine. */ 49: fd = open(*filename, O_RDONLY); 50: if (fd < 0) { 51: fprintf(stderr, "cannot open %s: %s\n", *filename, 52: strerror(errno)); 53: return 1; 54: } 55: 56: /* Sets the attributes as specified by the contents of 57: flags. */ 58: if (ioctl(fd, EXT3_IOC_SETFLAGS, &flags)) { 59: fprintf(stderr, "ioctl failed on %s: %s\n", *filename, 60: strerror(errno)); 61: return 1; 62: } 63: close(fd); 64: } 65: 66: return 0; 67: }; |