diff options
author | Richard Jones <rjones@redhat.com> | 2010-06-04 11:45:06 +0100 |
---|---|---|
committer | Richard Jones <rjones@redhat.com> | 2010-06-04 15:07:27 +0100 |
commit | 4df593496e116dfb635731c058b7627e81fc179c (patch) | |
tree | b2b03040026c9c47d545cb18ecfa08880ce86a65 /daemon/file.c | |
parent | 74958b0ad44df6ed703cd3009983d04ade3a8e93 (diff) | |
download | libguestfs-4df593496e116dfb635731c058b7627e81fc179c.tar.gz libguestfs-4df593496e116dfb635731c058b7627e81fc179c.tar.xz libguestfs-4df593496e116dfb635731c058b7627e81fc179c.zip |
file: Restrict to regular files (RHBZ#582484).
The file call can hang if called on char devices (because we are
using the file -s option).
This is hard to solve cleanly without adding another file API.
However this restricts file to regular files, unless called explicitly
with a /dev/ path. For non-regular files, it will now return a
string like "directory".
There is a small semantic change for symbolic links. Previously
it would not have worked at all on absolute links (or rather, the
results would have been undefined). It would have treated relative
symlinks to regular files as the regular file itself. Now it will
return the string "symbolic link" in both cases.
This commit also makes the API safe when called on untrusted
filesystems. Previously a filesystem might have been set up so
that (eg) /etc/redhat-release was a char device, which would have
caused virt-inspector and virt-v2v to hang. Now it will not hang.
Diffstat (limited to 'daemon/file.c')
-rw-r--r-- | daemon/file.c | 46 |
1 files changed, 35 insertions, 11 deletions
diff --git a/daemon/file.c b/daemon/file.c index 98244724..a55c6066 100644 --- a/daemon/file.c +++ b/daemon/file.c @@ -544,21 +544,45 @@ do_file (const char *path) return NULL; } path = buf; - } - /* file(1) manpage claims "file returns 0 on success, and non-zero on - * error", but this is evidently not true. It always returns 0, in - * every scenario I can think up. So check the target is readable - * first. - */ - if (access (path, R_OK) == -1) { - reply_with_perror ("access: %s", display_path); - free (buf); - return NULL; + /* For non-dev, check this is a regular file, else just return the + * file type as a string (RHBZ#582484). + */ + struct stat statbuf; + if (lstat (path, &statbuf) == -1) { + reply_with_perror ("lstat: %s", display_path); + free (buf); + return NULL; + } + + if (! S_ISREG (statbuf.st_mode)) { + char *ret; + + free (buf); + + if (S_ISDIR (statbuf.st_mode)) + ret = strdup ("directory"); + else if (S_ISCHR (statbuf.st_mode)) + ret = strdup ("character device"); + else if (S_ISBLK (statbuf.st_mode)) + ret = strdup ("block device"); + else if (S_ISFIFO (statbuf.st_mode)) + ret = strdup ("FIFO"); + else if (S_ISLNK (statbuf.st_mode)) + ret = strdup ("symbolic link"); + else if (S_ISSOCK (statbuf.st_mode)) + ret = strdup ("socket"); + else + ret = strdup ("unknown, not regular file"); + + if (ret == NULL) + reply_with_perror ("strdup"); + return ret; + } } char *out, *err; - int r = command (&out, &err, "file", "-zbsL", path, NULL); + int r = command (&out, &err, "file", "-zbs", path, NULL); free (buf); if (r == -1) { |