From f91c6bd76f58777c394f3d158a77dd768f6c9dd4 Mon Sep 17 00:00:00 2001 From: Guillem Jover Date: Tue, 20 Sep 2011 08:19:52 +0200 Subject: [PATCH] readlink.2: Document using st_size to allocate the buffer Signed-off-by: Michael Kerrisk --- man2/readlink.2 | 69 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/man2/readlink.2 b/man2/readlink.2 index c9f6d927b..2981a586f 100644 --- a/man2/readlink.2 +++ b/man2/readlink.2 @@ -35,7 +35,7 @@ .\" Modified Tue Jul 9 23:55:17 1996 by aeb .\" Modified Fri Jan 24 00:26:00 1997 by aeb .\" -.TH READLINK 2 2010-09-20 "Linux" "Linux Programmer's Manual" +.TH READLINK 2 2011-04-20 "Linux" "Linux Programmer's Manual" .SH NAME readlink \- read value of a symbolic link .SH SYNOPSIS @@ -69,6 +69,21 @@ does not append a null byte to It will truncate the contents (to a length of .I bufsiz characters), in case the buffer is too small to hold all of the contents. + +Using a statically sized buffer might not give enough room for the +symbolic link contents, which might end up getting truncated. The +needed size for the buffer can be obtained from the symbolic link's +.I stat +structure +.I st_size +field with +.BR lstat (), +but the number of written characters should be checked to make sure the +symbolic link did not change between both calls, and no truncation +happened. This also fixes a common portability problem when using +.I PATH_MAX +for the buffer size, as this is not guaranteed to be defined per POSIX +if the system does not have such limit. .SH "RETURN VALUE" On success, .BR readlink () @@ -130,6 +145,58 @@ was declared as Nowadays, the return type is declared as .IR ssize_t , as (newly) required in POSIX.1-2001. +.SH EXAMPLE +The following program allocates the space needed by +.BR readlink () +dynamically from the information provided by +.BR lstat (), +making sure there's no race condition between both calls. +.nf + +#include +#include +#include +#include +#include + +int +main(int argc, char *argv[]) +{ + struct stat sb; + char *linkname; + ssize_t r; + + if (argc != 2) { + fprintf(stderr, "Usage: %s \\n", argv[0]); + exit(EXIT_FAILURE); + } + + if (lstat(argv[1], &sb) == \-1) { + perror("lstat"); + exit(EXIT_FAILURE); + } + + linkname = malloc(sb.st_size + 1); + if (linkname == NULL) { + fprintf(stderr, "insufficient memory\\n"); + exit(EXIT_FAILURE); + } + + r = readlink(argv[1], linkname, sb.st_size + 1); + if (r < 0) { + perror("lstat"); + exit(EXIT_FAILURE); + } else if (r != sb.st_size) { + fprintf(stderr, "symlink changed between lstat and readlink\\n"); + exit(EXIT_FAILURE); + } + linkname[sb.st_size] = '\\0'; + + printf("'%s' points to '%s'\\n", argv[1], linkname); + + exit(EXIT_SUCCESS); +} +.fi .SH "SEE ALSO" .BR readlink (1), .BR lstat (2),