• Aucun résultat trouvé

Working with Symbolic Links

Dans le document What You Will Learn (Page 153-157)

Indirect System Calls

5.4. Obtaining Information about Files

5.4.5. Working with Symbolic Links

In general, symbolic links act like hard links; file operations such as open() and stat() apply to the pointed-to file instead of to the symbolic link itself. However, there are times when it really is necessary to work with the symbolic link instead of with the file the link points to.

For this reason, the lstat() system call exists. It behaves exactly like stat(), but if the file being checked happens to be a symbolic link, then the information returned applies to the symbolic link, and not to the pointed-to file. Specifically:

S_ISLNK(sbuf.st_mode) will be true.

sbuf.st_size is the number of bytes used by the name of the pointed-to file.

We already saw that the symlink() system call creates a symbolic link. But given an existing symbolic link, how can we retrieve the name of the file it points to? (ls obviously can, so we ought to be able to also.)

Opening the link with open() in order to read it with read() won't work; open() follows the link to the pointed-to file. Symbolic links thus necessitate an additional system call, named readlink():

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

readlink() places the contents of the symbolic link named by path into the buffer pointed to by buf. No more than bufsiz characters are copied. The return value is the number of characters placed in buf or -l if an error

occurred. readlink() does not supply the trailing zero byte.

Note that if the buffer passed in to readlink() is too small, you will lose information; the full name of the pointed-to file won't be available. To properly use readlink(), your code should do the following:

Use lstat() to verify that you have a symbolic link.

1.

Make sure that your buffer to hold the link contents is at least 'sbuf.st_size + 1' bytes big; the '+ 1' is for the trailing zero byte to turn the buffer into a usable C string.

2.

Call readlink(). It doesn't hurt to verify that the returned value is the same as sbuf.st_size. 3.

Assign '\0' to the byte after the contents of the link, to make it into a C string.

4.

Code to do all that would look something like this:

/* Error checking omitted for brevity */

int count;

char linkfile[PATH_MAX], realfile[PATH_MAX]; /* PATH_MAX is in <limits.h> */

strut stat sbuf;

... fill in linkfile with path to symbolic link of interest...

lstat(linkfile, & sbuf); Get stat information if (! S_ISLNK(sbuf.st_mode)) Check that it's a symlink /* not a symbolic link, handle it */

if (sbuf.st_size + 1 > PATH_MAX) Check buffer size /* handle buffer size problems */

count = readlink(linkfile, realfile, PATH_MAX); Read the link if (count != sbuf.st_size)

/* something weird going on, handle it */

realfile[count] = '\0'; Make it into a C string

This example uses fixed-size buffers for simplicity of presentation. Real code would use malloc() to allocate a buffer of the correct size since the fixed-size arrays might be too small. The file lib/xreadlink.c in the GNU Coreutils does just this. It reads the contents of a symbolic link into storage allocated by malloc(). We show here just the function; most of the file is boilerplate definitions. Line numbers are relative to the start of the file:

55 /* Call readlink to get the symbolic link value of FILENAME.

56 Return a pointer to that NUL-terminated string in malloc'd storage.

57 If readlink fails, return NULL (caller may use errno to diagnose).

58 If realloc fails, or if the link value is longer than SIZE_MAX :-), 65 detects arithmetic overflow earlier, but is not required. */

66 size_t buf_size = 128;

67

68 while (1) 69 {

70 char *buffer = xmalloc (buf_size);

71 ssize_t link_length = readlink (filename, buffer, buf_size);

72

The function body consists of an infinite loop (lines 68–91), broken at line 84 which returns the allocated buffer.

The loop starts by allocating an initial buffer (line 70) and reading the link (line 71). Lines 73–79 handle the error case, saving and restoring errno so that it can be used correctly by the calling code.

Lines 81–85 handle the "success" case, in which the link's contents' length is smaller than the buffer size. In this case, the terminating zero is supplied (line 83) and then the buffer returned (line 84), breaking the infinite loop.

This ensures that the entire link contents have been placed into the buffer, since readlink() has no way to indicate "insufficient space in buffer."

Lines 87–88 free the buffer and double the buffer size for the next try at the top of the loop. Lines 89–90 handle the case in which the link's size is too big: buf_size is greater than SSIZE_MAX, or SSIZE_MAX is larger than the value that can be represented in a signed integer of the same size as used to hold SIZE_MAX and buf_size has wrapped around to zero. (These are unlikely conditions, but strange things do happen.) If either condition is true, the program dies with an error message. Otherwise, the function continues around to the top of the loop to make another try at allocating a buffer and reading the link.

Some further explanation: The 'SIZE_MAX / 2 < SSIZE_MAX' condition is true only on systems on which 'SIZE_MAX < 2 * SSIZE_MAX'; we don't know of any, but only on such a system can buf_size wrap around to zero. Since in practice this condition can't be true, the compiler can optimize away the whole expression, including the following 'buf_size == 0' test. After reading this code, you might ask, "Why not use lstat() to retrieve the size of the symbolic link, allocate a buffer of the right size with malloc(), and be done?" Well, there are a number of reasons.[9]

[9] Thanks to Jim Meyering for explaining the issues.

lstat() is a system call—it's best to avoid the overhead of making it since the contents of most symbolic links will fit in the initial buffer size of 128.

Calling lstat() introduces a race condition: The link could change between the execution of lstat() and

readlink(), forcing the need to iterate anyway.

Some systems don't properly fill in the st_size member for symbolic links. (Sad, but true.) In a similar fashion, as we see in Section 8.4.2, "Getting the Current Directory: getcwd()," page 258, Linux provides

special symbolic links under /proc whose st_size is zero, but for which readlink() does return valid content.

Finally, when the buffer isn't big enough, xreadlink() uses free() and malloc() with a bigger size, instead of

realloc(), to avoid the useless copying that realloc() does. (The comment on line 58 is thus out of date since realloc() isn't being used; this is fixed in the post-5.0 version of the Coreutils.)

5.5. Changing Ownership, Permission, and Modification

Dans le document What You Will Learn (Page 153-157)