4.22. chdir, fchdir, and getcwd FunctionsEvery process has a current working directory. This directory is where the search for all relative pathnames starts (all pathnames that do not begin with a slash). When a user logs in to a UNIX system, the current working directory normally starts at the directory specified by the sixth field in the /etc/passwd filethe user's home directory. The current working directory is an attribute of a process; the home directory is an attribute of a login name. We can change the current working directory of the calling process by calling the chdir or fchdir functions.
We can specify the new current working directory either as a pathname or through an open file descriptor.
ExampleBecause it is an attribute of a process, the current working directory cannot affect processes that invoke the process that executes the chdir. (We describe the relationship between processes in more detail in Chapter 8.) This means that the program in Figure 4.23 doesn't do what we might expect. If we compile it and call the executable mycd, we get the following: $ pwd /usr/lib $ mycd chdir to /tmp succeeded $ pwd /usr/lib The current working directory for the shell that executed the mycd program didn't change. This is a side effect of the way that the shell executes programs. Each program is run in a separate process, so the current working directory of the shell is unaffected by the call to chdir in the program. For this reason, the chdir function has to be called directly from the shell, so the cd command is built into the shells. Figure 4.23. Example of chdir function#include "apue.h" int main(void) { if (chdir("/tmp") < 0) err_sys("chdir failed"); printf("chdir to /tmp succeeded\n"); exit(0); } Because the kernel must maintain knowledge of the current working directory, we should be able to fetch its current value. Unfortunately, the kernel doesn't maintain the full pathname of the directory. Instead, the kernel keeps information about the directory, such as a pointer to the directory's v-node. What we need is a function that starts at the current working directory (dot) and works its way up the directory hierarchy, using dot-dot to move up one level. At each directory, the function reads the directory entries until it finds the name that corresponds to the i-node of the directory that it just came from. Repeating this procedure until the root is encountered yields the entire absolute pathname of the current working directory. Fortunately, a function is already provided for us that does this task.
We must pass to this function the address of a buffer, buf, and its size (in bytes). The buffer must be large enough to accommodate the absolute pathname plus a terminating null byte, or an error is returned. (Recall the discussion of allocating space for a maximum-sized pathname in Section 2.5.5.)
ExampleThe program in Figure 4.24 changes to a specific directory and then calls getcwd to print the working directory. If we run the program, we get $ ./a.out cwd = /var/spool/uucppublic $ ls -l /usr/spool lrwxrwxrwx 1 root 12 Jan 31 07:57 /usr/spool -> ../var/spool Note that chdir follows the symbolic linkas we expect it to, from Figure 4.17but when it goes up the directory tree, getcwd has no idea when it hits the /var/spool directory that it is pointed to by the symbolic link /usr/spool. This is a characteristic of symbolic links. Figure 4.24. Example of getcwd function#include "apue.h" int main(void) { char *ptr; int size; if (chdir("/usr/spool/uucppublic") < 0) err_sys("chdir failed"); ptr = path_alloc(&size); /* our own function */ if (getcwd(ptr, size) == NULL) err_sys("getcwd failed"); printf("cwd = %s\n", ptr); exit(0); } The getcwd function is useful when we have an application that needs to return to the location in the file system where it started out. We can save the starting location by calling getcwd before we change our working directory. After we complete our processing, we can pass the pathname obtained from getcwd to chdir to return to our starting location in the file system. The fchdir function provides us with an easy way to accomplish this task. Instead of calling getcwd, we can open the current directory and save the file descriptor before we change to a different location in the file system. When we want to return to where we started, we can simply pass the file descriptor to fchdir. |