| .\" Copyright 1983-1991, The Regents of the University of California. |
| .\" Copyright, the authors of the Linux man-pages project |
| .\" |
| .\" SPDX-License-Identifier: BSD-4-Clause-UC |
| .\" |
| .TH readlink 2 (date) "Linux man-pages (unreleased)" |
| .SH NAME |
| readlink, readlinkat \- read value of a symbolic link |
| .SH LIBRARY |
| Standard C library |
| .RI ( libc ,\~ \-lc ) |
| .SH SYNOPSIS |
| .nf |
| .B #include <unistd.h> |
| .P |
| .BR "ssize_t readlink(" "size_t bufsiz;" |
| .BI " const char *restrict " path , |
| .BI " char " buf "[restrict " bufsiz "], size_t " bufsiz ); |
| .P |
| .BR "#include <fcntl.h> " "/* Definition of " AT_* " constants */" |
| .B #include <unistd.h> |
| .P |
| .BR "ssize_t readlinkat(" "size_t bufsiz;" |
| .BI " int " dirfd ", const char *restrict " path , |
| .BI " char " buf "[restrict " bufsiz "], size_t " bufsiz ); |
| .P |
| .fi |
| .RS -4 |
| Feature Test Macro Requirements for glibc (see |
| .BR feature_test_macros (7)): |
| .RE |
| .P |
| .BR readlink (): |
| .nf |
| _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200112L |
| .\" || _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED |
| || /* glibc <= 2.19: */ _BSD_SOURCE |
| .fi |
| .P |
| .BR readlinkat (): |
| .nf |
| Since glibc 2.10: |
| _POSIX_C_SOURCE >= 200809L |
| Before glibc 2.10: |
| _ATFILE_SOURCE |
| .fi |
| .SH DESCRIPTION |
| .BR readlink () |
| places the contents of the symbolic link |
| .I path |
| in the buffer |
| .IR buf , |
| which has size |
| .IR bufsiz . |
| .BR readlink () |
| does not append a terminating null byte to |
| .IR buf . |
| It will (silently) truncate the contents (to a length of |
| .I bufsiz |
| characters), in case the buffer is too small to hold all of the contents. |
| .SS readlinkat() |
| The |
| .BR readlinkat () |
| system call operates in exactly the same way as |
| .BR readlink (), |
| except for the differences described here. |
| .P |
| If |
| .I path |
| is relative, then it is interpreted relative to the directory |
| referred to by the file descriptor |
| .I dirfd |
| (rather than relative to the current working directory of |
| the calling process, as is done by |
| .BR readlink () |
| for a relative pathname). |
| .P |
| If |
| .I path |
| is relative and |
| .I dirfd |
| is the special value |
| .BR AT_FDCWD , |
| then |
| .I path |
| is interpreted relative to the current working |
| directory of the calling process (like |
| .BR readlink ()). |
| .P |
| If |
| .I path |
| is absolute, then |
| .I dirfd |
| is ignored. |
| .P |
| Since Linux 2.6.39, |
| .\" commit 65cfc6722361570bfe255698d9cd4dccaf47570d |
| .I path |
| can be an empty string, |
| in which case the call operates on the symbolic link referred to by |
| .I dirfd |
| (which should have been obtained using |
| .BR open (2) |
| with the |
| .B O_PATH |
| and |
| .B O_NOFOLLOW |
| flags). |
| .P |
| See |
| .BR openat (2) |
| for an explanation of the need for |
| .BR readlinkat (). |
| .SH RETURN VALUE |
| On success, these calls return the number of bytes placed in |
| .IR buf . |
| (If the returned value equals |
| .IR bufsiz , |
| then truncation may have occurred.) |
| On error, \-1 is returned and |
| .I errno |
| is set to indicate the error. |
| .SH ERRORS |
| .TP |
| .B EACCES |
| Search permission is denied for a component of the path prefix. |
| (See also |
| .BR path_resolution (7).) |
| .TP |
| .B EBADF |
| .RB ( readlinkat ()) |
| .I path |
| is relative but |
| .I dirfd |
| is neither |
| .B AT_FDCWD |
| nor a valid file descriptor. |
| .TP |
| .B EFAULT |
| .I buf |
| extends outside the process's allocated address space. |
| .TP |
| .B EINVAL |
| .I bufsiz |
| is not positive. |
| .\" At the glibc level, bufsiz is unsigned, so this error can only occur |
| .\" if bufsiz==0. However, the in the kernel syscall, bufsiz is signed, |
| .\" and this error can also occur if bufsiz < 0. |
| .\" See: http://thread.gmane.org/gmane.linux.man/380 |
| .\" Subject: [patch 0/3] [RFC] kernel/glibc mismatch of "readlink" syscall? |
| .TP |
| .B EINVAL |
| The named file (i.e., the final filename component of |
| .IR path ) |
| is not a symbolic link. |
| .TP |
| .B EIO |
| An I/O error occurred while reading from the filesystem. |
| .TP |
| .B ELOOP |
| Too many symbolic links were encountered in translating the pathname. |
| .TP |
| .B ENAMETOOLONG |
| A pathname, or a component of a pathname, was too long. |
| .TP |
| .B ENOENT |
| The named file does not exist. |
| .TP |
| .B ENOMEM |
| Insufficient kernel memory was available. |
| .TP |
| .B ENOTDIR |
| A component of the path prefix is not a directory. |
| .TP |
| .B ENOTDIR |
| .RB ( readlinkat ()) |
| .I path |
| is relative and |
| .I dirfd |
| is a file descriptor referring to a file other than a directory. |
| .SH STANDARDS |
| POSIX.1-2008. |
| .SH HISTORY |
| .TP |
| .BR readlink () |
| 4.4BSD |
| (first appeared in 4.2BSD), |
| POSIX.1-2001, POSIX.1-2008. |
| .TP |
| .BR readlinkat () |
| POSIX.1-2008. |
| Linux 2.6.16, |
| glibc 2.4. |
| .P |
| Up to and including glibc 2.4, the return type of |
| .BR readlink () |
| was declared as |
| .IR int . |
| Nowadays, the return type is declared as |
| .IR ssize_t , |
| as (newly) required in POSIX.1-2001. |
| .SS glibc |
| On older kernels where |
| .BR readlinkat () |
| is unavailable, the glibc wrapper function falls back to the use of |
| .BR readlink (). |
| When |
| .I path |
| is relative, |
| glibc constructs a pathname based on the symbolic link in |
| .I /proc/self/fd |
| that corresponds to the |
| .I dirfd |
| argument. |
| .SH NOTES |
| Using a statically sized buffer might not provide enough room for the |
| symbolic link contents. |
| The required size for the buffer can be obtained from the |
| .I stat.st_size |
| value returned by a call to |
| .BR lstat (2) |
| on the link. |
| However, the number of bytes written by |
| .BR readlink () |
| and |
| .BR readlinkat () |
| should be checked to make sure that the size of the |
| symbolic link did not increase between the calls. |
| Dynamically allocating the buffer for |
| .BR readlink () |
| and |
| .BR readlinkat () |
| also addresses a common portability problem when using |
| .B PATH_MAX |
| for the buffer size, |
| as this constant is not guaranteed to be defined per POSIX |
| if the system does not have such limit. |
| .SH EXAMPLES |
| The following program allocates the buffer needed by |
| .BR readlink () |
| dynamically from the information provided by |
| .BR lstat (2), |
| falling back to a buffer of size |
| .B PATH_MAX |
| in cases where |
| .BR lstat (2) |
| reports a size of zero. |
| .P |
| .\" SRC BEGIN (readlink.c) |
| .EX |
| #include <limits.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| \& |
| int |
| main(int argc, char *argv[]) |
| { |
| char *buf; |
| ssize_t nbytes, bufsiz; |
| struct stat sb; |
| \& |
| if (argc != 2) { |
| fprintf(stderr, "Usage: %s <path>\[rs]n", argv[0]); |
| exit(EXIT_FAILURE); |
| } |
| \& |
| if (lstat(argv[1], &sb) == \-1) { |
| perror("lstat"); |
| exit(EXIT_FAILURE); |
| } |
| \& |
| /* Add one to the link size, so that we can determine whether |
| the buffer returned by readlink() was truncated. */ |
| \& |
| bufsiz = sb.st_size + 1; |
| \& |
| /* Some magic symlinks under (for example) /proc and /sys |
| report \[aq]st_size\[aq] as zero. In that case, take PATH_MAX as |
| a "good enough" estimate. */ |
| \& |
| if (sb.st_size == 0) |
| bufsiz = PATH_MAX; |
| \& |
| buf = malloc(bufsiz); |
| if (buf == NULL) { |
| perror("malloc"); |
| exit(EXIT_FAILURE); |
| } |
| \& |
| nbytes = readlink(argv[1], buf, bufsiz); |
| if (nbytes == \-1) { |
| perror("readlink"); |
| exit(EXIT_FAILURE); |
| } |
| \& |
| /* Print only \[aq]nbytes\[aq] of \[aq]buf\[aq], as it doesn't contain a terminating |
| null byte (\[aq]\[rs]0\[aq]). */ |
| printf("\[aq]%s\[aq] points to \[aq]%.*s\[aq]\[rs]n", argv[1], (int) nbytes, buf); |
| \& |
| /* If the return value was equal to the buffer size, then |
| the link target was larger than expected (perhaps because the |
| target was changed between the call to lstat() and the call to |
| readlink()). Warn the user that the returned target may have |
| been truncated. */ |
| \& |
| if (nbytes == bufsiz) |
| printf("(Returned buffer may have been truncated)\[rs]n"); |
| \& |
| free(buf); |
| exit(EXIT_SUCCESS); |
| } |
| .EE |
| .\" SRC END |
| .SH SEE ALSO |
| .BR readlink (1), |
| .BR lstat (2), |
| .BR stat (2), |
| .BR symlink (2), |
| .BR realpath (3), |
| .BR path_resolution (7), |
| .BR symlink (7) |