Section 11.4. Manipulating Directory Entries


11.4. Manipulating Directory Entries

Remember that directory entries (file names) are nothing more than pointers to on-disk inodes; almost all the important information concerning a file is stored in the inode. open() lets a process create directory entries that are regular files, but other functions are needed to create other types of files and to manipulate the entries themselves. The functions that allow you to create, remove, and search directories are covered in Chapter 14; socket files are introduced in Chapter 17. This section covers symbolic links, device files, and FIFOs.

11.4.1. Creating Device and Named Pipe Entries

Processes create device file entries and named pipes in the file system through mknod().

 #include <fcntl.h> #include <unistd.h> int mknod(const char * pathname, mode_t mode, dev_t dev); 

The pathname is the file name to create, mode is both the access mode of the new file (which gets modified by the current umask) and the new file type (S_IFIFO, S_IFBLK, or S_IFCHR). The final parameter, dev, contains the major and minor numbers of the device to create. The type of device (character or block) and the major number of the device tell the kernel which device driver is responsible for operations on that device file. The minor number is used internally by the device driver to differentiate among multiple devices it provides. Only the root user is allowed to create device files; all users may create named pipes.

The <sys/sysmacros.h> header file provides three macros for manipulating dev_t values. The makedev() macro takes a major number as its first argument and a minor number as its second, and it returns a dev_t suitable for mknod(). The major() and minor() macros take a dev_t value as their sole argument and return the device's major and minor numbers, respectively.

The mknod program available under Linux provides a user-level interface to the mknod() system call (see man 1 mknod for details). Here is a simple reimplementation of mknod to illustrate the mknod() system call. Notice that the program creates the file with mode 0666 (giving read and write access to all users), and it depends on the process's umask setting to get the permissions right.

  1: /* mknod.c */  2:  3: /* Create the device or named pipe specified on the command line.  4:    See the mknod(1) man page for details on the command line  5:    parameters. */  6:  7: #include <errno.h>  8: #include <stdio.h>  9: #include <stdlib.h> 10: #include <string.h> 11: #include <sys/stat.h> 12: #include <sys/sysmacros.h> 13: #include <unistd.h> 14: 15: void usage(void) { 16:      fprintf(stderr, "usage: mknod <path> [b|c|u|p] " 17:                      "<major> <minor>\n"); 18:      exit(1); 19: } 20: 21: int main(int argc, const char ** argv) { 22:     int major = 0, minor = 0; 23:     const char * path; 24:     int mode = 0666; 25:     char *end; 26:     int args; 27: 28:     /* We always need at least the type of inode to create, and 29:        the path for it. */ 30:     if (argc < 3) usage(); 31: 32:     path = argv[1]; 33: 34:     /* the second argument tells us the type of node to create */ 35:     if (!strcmp(argv[2], "b")) { 36:         mode |= S_IFBLK; 37:         args = 5; 38:     } else if (!strcmp(argv[2], "c") || !strcmp(argv[2], "u")) { 39:         mode |= S_IFCHR; 40:         args = 5; 41:     } else if (!strcmp(argv[2], "p")) { 42:         mode |= S_IFIFO; 43:         args = 3; 44:     } else { 45:         fprintf(stderr, "unknown node type %s\n", argv[2]); 46:         return 1; 47:     } 48: 49:     /* args tells us how many parameters to expect, as we need more 50:        information to create device files than named pipes */ 51:     if (argc != args) usage(); 52: 53:     if (args == 5) { 54:         /* get the major and minor numbers for the device file to 55:            create */ 56:         major = strtol(argv[3], &end, 0); 57:         if (*end) { 58:             fprintf(stderr, "bad major number %s\n", argv[3]); 59:             return 1; 60:         } 61: 62:         minor = strtol(argv[4], &end, 0); 63:         if (*end) { 64:             fprintf(stderr, "bad minor number %s\n", argv[4]); 65:             return 1; 66:         } 67:     } 68: 69:     /* if we're creating a named pipe, the final parameter is 70:        ignored */ 71:     if (mknod(path, mode, makedev(major, minor))) { 72:         fprintf(stderr, "mknod failed: %s\n", strerror(errno)); 73:         return 1; 74:     } 75: 76:     return 0; 77: } 

11.4.2. Creating Hard Links

When multiple file names in the file system refer to a single inode, the files are called hard links to one other. All the file names must reside in the same physical file system (normally, this means they must all be on the same device). When a file has multiple hard links, each of those file names is an exact peer there is no way to tell which file name was originally used. One benefit of this model is that removing one hard link does not remove the file from the device it stays until all the links to it are removed. The link() system call links a new file name to an existing inode.

 #include <unistd.h> int link(const char * origpath, const char * newpath); 

The origpath refers to a pathname that already exists; newpath is the path for the new hard link. Any user may create a link to a file he has read access to, as long as he has write access for the directory in which he is creating the link and execute permissions on the directory origpath is in. Only the root user has ever been allowed to create hard links to directories, but doing so was generally a bad idea, because most file systems and several utilities do not handle it very well; it is now entirely disallowed.

11.4.3. Using Symbolic Links

Symbolic links are a more flexible type of link than hard links, but they do not share the peer relationship that hard links enjoy. Whereas hard links share an inode, symbolic links point to other file names. If the destination file name is removed, the symbolic link then points to a file that does not exist, resulting in a dangling link. Using symbolic links between subdirectories is common, and symbolic links may also cross physical file system boundaries, which hard links cannot.

Almost all system calls that access files by their pathname automatically follow symbolic links to find the proper inode. The following calls do not follow symbolic links under Linux:

  • chown()

  • lstat()

  • readlink()

  • rename()

  • unlink()

Creating symbolic links is just like creating hard links, but the symlink() system call is used instead.

 #include <unistd.h> int symlink(const char * origpath, const char * newpath); 

If the call is successful, the file newpath gets created as a symbolic link pointing to origpath (often newpath is said to contain oldpath as its value).

Finding the value of the symbolic link is a bit more complicated.

 #include <unistd.h> int readlink(const char * pathname, char * buf,              size_t bufsiz); 

The buffer that buf points to is filled in by the contents of the pathname symlink as long as buf is long enough to hold the contents. bufsize should contain the length of buf in bytes. Usually, the PATH_MAX constant is used for the size of the buffer, as that should be big enough to hold the contents of any symlink.[20] One oddity of readlink() is that it does not end the string it writes to buf with a '\0' character, so buf is not a valid C string even if readlink() succeeds. It instead returns the number of bytes written to buf on success, and -1 on error. Thanks to this quirk, code that uses readlink() often looks like this:

[20] Although PATH_MAX is not guaranteed to be large enough, it is, for all practical purposes. If you are dealing with pathological cases, you should call readlink() iteratively, making the buffer bigger until readlink() returns a value smaller than bufsiz.

 char buf[PATH_MAX + 1]; int bytes; if ((bytes = readlink(pathname, buf, sizeof(buf) - 1)) < 0) {     perror("error in readlink"); } else {     buf[bytes] = '\0'; } 

11.4.4. Removing Files

Removing a file removes the pointer to its inode and removes the file's data if there are no other hard links to the file. If any processes have the file open, the file's inode is preserved until the final process closes the file, and then the inode and the file's data are both discarded. As there is no way of forcing a file to be removed immediately, this operation is called unlinking the file, since it removes a file name/inode link.

 #include <unistd.h> int unlink(char * pathname); 

11.4.5. Renaming Files

A file name may be changed to any other file name as long as both names are on the same physical partition (this is the same limit that applies to creating hard links). If the new file name already references a file, that name is unlinked before the move takes place. The rename() system call is guaranteed to be atomic. Other processes on the system always see the file in existence under one name or the other; there is no point at which the file does not exist under either name, nor under both names. As open files are not related to file names (only to inodes), renaming a file that other processes have open does not affect those other processes in any way. Here is what the system call looks like:

 #include <unistd.h> int rename(const char * oldpath, const char * newpath); 

After the call, the file referenced by oldpath may be referenced by newpath, but not by oldpath.


    Linux Application Development
    Linux Application Development (paperback) (2nd Edition)
    ISBN: 0321563220
    EAN: 2147483647
    Year: 2003
    Pages: 168 © 2008-2017.
    If you may any questions please contact us: