2
0
mirror of https://github.com/openvswitch/ovs synced 2025-09-02 15:25:22 +00:00

util: New function follow_symlinks().

It will acquire its first user in an upcoming commit.

Signed-off-by: Ben Pfaff <blp@nicira.com>
This commit is contained in:
Ben Pfaff
2012-07-30 11:36:06 -07:00
parent 8c2296a6d9
commit fee0c96314
4 changed files with 198 additions and 0 deletions

View File

@@ -24,6 +24,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include "byte-order.h" #include "byte-order.h"
#include "coverage.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);
ssize_t retval = readlink(filename, buf, size);
int error = errno;
if (retval >= 0 && retval < size) {
buf[retval] = '\0';
return buf;
}
free(buf);
if (retval < 0) {
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++) {
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 /* Pass a value to this function if it is marked with
* __attribute__((warn_unused_result)) and you genuinely want to ignore * __attribute__((warn_unused_result)) and you genuinely want to ignore

View File

@@ -219,6 +219,9 @@ char *dir_name(const char *file_name);
char *base_name(const char *file_name); char *base_name(const char *file_name);
char *abs_file_name(const char *dir, 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); void ignore(bool x OVS_UNUSED);
int log_2_floor(uint32_t); int log_2_floor(uint32_t);
int log_2_ceil(uint32_t); int log_2_ceil(uint32_t);

View File

@@ -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([dir/file//], [dir], [file]) CHECK_FILE_NAME([dir/file//], [dir], [file])
CHECK_FILE_NAME([///foo], [/], [foo]) 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

View File

@@ -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[] = { static const struct command commands[] = {
{"ctz", 0, 0, test_ctz}, {"ctz", 0, 0, test_ctz},
@@ -275,6 +287,7 @@ static const struct command commands[] = {
{"bitwise_zero", 0, 0, test_bitwise_zero}, {"bitwise_zero", 0, 0, test_bitwise_zero},
{"bitwise_one", 0, 0, test_bitwise_one}, {"bitwise_one", 0, 0, test_bitwise_one},
{"bitwise_is_all_zeros", 0, 0, test_bitwise_is_all_zeros}, {"bitwise_is_all_zeros", 0, 0, test_bitwise_is_all_zeros},
{"follow-symlinks", 1, INT_MAX, test_follow_symlinks},
{NULL, 0, 0, NULL}, {NULL, 0, 0, NULL},
}; };