[ovs-dev] [debian 7/9] util: New function follow_symlinks().

Ansis Atteka aatteka at nicira.com
Tue Jul 31 17:06:33 UTC 2012


On Mon, Jul 30, 2012 at 3:18 PM, Ben Pfaff <blp at nicira.com> wrote:

> It will acquire its first user in an upcoming commit.
>
> Signed-off-by: Ben Pfaff <blp at 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 at openvswitch.org
> http://openvswitch.org/mailman/listinfo/dev
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openvswitch.org/pipermail/ovs-dev/attachments/20120731/149b7011/attachment-0003.html>


More information about the dev mailing list