On Mon, Jul 30, 2012 at 3:18 PM, Ben Pfaff <b...@nicira.com> wrote: > It will acquire its first user in an upcoming commit. > > Signed-off-by: Ben Pfaff <b...@nicira.com> > --- > lib/util.c | 85 +++++++++++++++++++++++++++++++++++++++++++++ > lib/util.h | 3 ++ > tests/file_name.at | 97 > ++++++++++++++++++++++++++++++++++++++++++++++++++++ > tests/test-util.c | 13 +++++++ > 4 files changed, 198 insertions(+), 0 deletions(-) > > diff --git a/lib/util.c b/lib/util.c > index cbcf693..603e482 100644 > --- a/lib/util.c > +++ b/lib/util.c > @@ -24,6 +24,7 @@ > #include <stdio.h> > #include <stdlib.h> > #include <string.h> > +#include <sys/stat.h> > #include <unistd.h> > #include "byte-order.h" > #include "coverage.h" > @@ -647,6 +648,90 @@ abs_file_name(const char *dir, const char *file_name) > } > } > > +/* Like readlink(), but returns the link name as a null-terminated string > in > + * allocated memory that the caller must eventually free (with free()). > + * Returns NULL on error, in which case errno is set appropriately. */ > +char * > +xreadlink(const char *filename) > +{ > + size_t size; > + > + for (size = 64; ; size *= 2) { > + char *buf = xmalloc(size); > + int retval = readlink(filename, buf, size); > + int error = errno; > + > + if (retval >= 0 && retval < size) { > + buf[retval] = '\0'; > + return buf; > + } > + > + free(buf); > + if (retval < size) { > Shouldn't size be of type ssize_t instead? Otherwise this could loop forever, if readlink() returned -1.
> + errno = error; > + return NULL; > + } > + } > +} > + > +/* Returns a version of 'filename' with symlinks in the final component > + * dereferenced. This differs from realpath() in that: > + * > + * - 'filename' need not exist. > + * > + * - If 'filename' does exist as a symlink, its referent need not > exist. > + * > + * - Only symlinks in the final component of 'filename' are > dereferenced. > + * > + * The caller must eventually free the returned string (with free()). */ > +char * > +follow_symlinks(const char *filename) > +{ > + struct stat s; > + char *fn; > + int i; > + > + fn = xstrdup(filename); > + for (i = 0; i < 10; i++) { > Is 10 the max level of symlinks we can handle? Maybe define this as a constant? > + char *linkname; > + char *next_fn; > + > + if (lstat(fn, &s) != 0 || !S_ISLNK(s.st_mode)) { > + return fn; > + } > + > + linkname = xreadlink(fn); > + if (!linkname) { > + VLOG_WARN("%s: readlink failed (%s)", filename, > strerror(errno)); > + return fn; > + } > + > + if (linkname[0] == '/') { > + /* Target of symlink is absolute so use it raw. */ > + next_fn = linkname; > + } else { > + /* Target of symlink is relative so add to 'fn''s directory. > */ > + char *dir = dir_name(fn); > + > + if (!strcmp(dir, ".")) { > + next_fn = linkname; > + } else { > + char *separator = dir[strlen(dir) - 1] == '/' ? "" : "/"; > + next_fn = xasprintf("%s%s%s", dir, separator, linkname); > + free(linkname); > + } > + > + free(dir); > + } > + > + free(fn); > + fn = next_fn; > + } > + > + VLOG_WARN("%s: too many levels of symlinks", filename); > + free(fn); > + return xstrdup(filename); > +} > > /* Pass a value to this function if it is marked with > * __attribute__((warn_unused_result)) and you genuinely want to ignore > diff --git a/lib/util.h b/lib/util.h > index 809ae9c..60ec737 100644 > --- a/lib/util.h > +++ b/lib/util.h > @@ -219,6 +219,9 @@ char *dir_name(const char *file_name); > char *base_name(const char *file_name); > char *abs_file_name(const char *dir, const char *file_name); > > +char *xreadlink(const char *filename); > +char *follow_symlinks(const char *filename); > + > void ignore(bool x OVS_UNUSED); > int log_2_floor(uint32_t); > int log_2_ceil(uint32_t); > diff --git a/tests/file_name.at b/tests/file_name.at > index e0b43dc..aee4070 100644 > --- a/tests/file_name.at > +++ b/tests/file_name.at > @@ -24,3 +24,100 @@ CHECK_FILE_NAME([dir/file], [dir], [file]) > CHECK_FILE_NAME([dir/file/], [dir], [file]) > CHECK_FILE_NAME([dir/file//], [dir], [file]) > CHECK_FILE_NAME([///foo], [/], [foo]) > + > +AT_BANNER([test follow_symlinks function]) > + > +m4_define([CHECK_FOLLOW], > + [echo "check $1 -> $2" > + AT_CHECK_UNQUOTED([test-util follow-symlinks "$1"], [0], [$2 > +]) > + echo]) > + > +AT_SETUP([follow_symlinks - relative symlinks]) > +: > target > +ln -s target source > +AT_SKIP_IF([test ! -h source]) > +CHECK_FOLLOW([source], [target]) > + > +mkdir dir > +ln -s target2 dir/source2 > +CHECK_FOLLOW([dir/source2], [dir/target2]) > + > +mkdir dir/dir2 > +ln -s dir/b a > +ln -s c dir/b > +ln -s dir2/d dir/c > +CHECK_FOLLOW([a], [dir/dir2/d]) > +AT_CLEANUP > + > +AT_SETUP([follow_symlinks - absolute symlinks]) > +: > target > +ln -s "`pwd`/target" source > +AT_SKIP_IF([test ! -h source]) > +CHECK_FOLLOW([source], [`pwd`/target]) > + > +mkdir dir > +ln -s "`pwd`/dir/target2" dir/source2 > +CHECK_FOLLOW([dir/source2], [`pwd`/dir/target2]) > + > +mkdir dir/dir2 > +ln -s "`pwd`/dir/b" a > +ln -s "`pwd`/dir/c" dir/b > +ln -s "`pwd`/dir/dir2/d" dir/c > +CHECK_FOLLOW([a], [`pwd`/dir/dir2/d]) > +AT_CLEANUP > + > +AT_SETUP([follow_symlinks - symlinks to directories]) > +mkdir target > +ln -s target source > +AT_SKIP_IF([test ! -h source]) > +ln -s target/ source2 > +CHECK_FOLLOW([source], [target]) > +CHECK_FOLLOW([source2], [target/]) > + > +# follow_symlinks() doesn't expand symlinks in the middle of a name. > +: > source/x > +CHECK_FOLLOW([source/x], [source/x]) > +AT_CLEANUP > + > +AT_SETUP([follow_symlinks - nonexistent targets]) > +ln -s target source > +AT_SKIP_IF([test ! -h source]) > +CHECK_FOLLOW([source], [target]) > +CHECK_FOLLOW([target], [target]) > +CHECK_FOLLOW([target], [target]) > +AT_CLEANUP > + > +AT_SETUP([follow_symlinks - regular files]) > +touch x > +CHECK_FOLLOW([x], [x]) > +AT_CLEANUP > + > +AT_SETUP([follow_symlinks - device targets]) > +AT_SKIP_IF([test ! -e /dev/null]) > +AT_SKIP_IF([test ! -e /dev/full]) > +ln -s /dev/null x > +ln -s /dev/full y > +CHECK_FOLLOW([x], [/dev/null]) > +CHECK_FOLLOW([y], [/dev/full]) > +AT_CLEANUP > + > +AT_SETUP([follow_symlinks - nonexistent files]) > +CHECK_FOLLOW([nonexistent], [nonexistent]) > +CHECK_FOLLOW([a/b/c], [a/b/c]) > +CHECK_FOLLOW([/a/b/c], [/a/b/c]) > +CHECK_FOLLOW([//a/b/c], [//a/b/c]) > +AT_CLEANUP > + > +AT_SETUP([follow_symlinks - symlink loop]) > +ln -s a b > +AT_SKIP_IF([test ! -h b]) > +ln -s b a > +AT_SKIP_IF([test ! -h a]) > + > +AT_CHECK([test-util follow-symlinks a], [0], [a > +], [stderr]) > +AT_CHECK([sed 's/^[[^|]]*|//' stderr], [0], > + [00001|util|WARN|a: too many levels of symlinks > +]) > +AT_CLEANUP > diff --git a/tests/test-util.c b/tests/test-util.c > index 56c5b28..71a7f21 100644 > --- a/tests/test-util.c > +++ b/tests/test-util.c > @@ -267,6 +267,18 @@ test_bitwise_is_all_zeros(int argc OVS_UNUSED, char > *argv[] OVS_UNUSED) > } > } > } > + > +static void > +test_follow_symlinks(int argc, char *argv[]) > +{ > + int i; > + > + for (i = 1; i < argc; i++) { > + char *target = follow_symlinks(argv[i]); > + puts(target); > + free(target); > + } > +} > > static const struct command commands[] = { > {"ctz", 0, 0, test_ctz}, > @@ -275,6 +287,7 @@ static const struct command commands[] = { > {"bitwise_zero", 0, 0, test_bitwise_zero}, > {"bitwise_one", 0, 0, test_bitwise_one}, > {"bitwise_is_all_zeros", 0, 0, test_bitwise_is_all_zeros}, > + {"follow-symlinks", 1, INT_MAX, test_follow_symlinks}, > {NULL, 0, 0, NULL}, > }; > > -- > 1.7.2.5 > > _______________________________________________ > dev mailing list > dev@openvswitch.org > http://openvswitch.org/mailman/listinfo/dev >
_______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev