X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=daemon%2Frealpath.c;h=89f77378d432b05c4bcfe01906ffcd5987847b50;hp=9ab478d2f3582798361addc6fe7fc7e54dda5005;hb=d600342b7d29c0176ff96a7807ebb38303ecb3a6;hpb=7c3a90f94cd6b8fec9cdd1c052b91a14c0ee0e0e diff --git a/daemon/realpath.c b/daemon/realpath.c index 9ab478d..89f7737 100644 --- a/daemon/realpath.c +++ b/daemon/realpath.c @@ -22,26 +22,178 @@ #include #include #include +#include #include +#include +#include #include "daemon.h" +#include "optgroups.h" #include "actions.h" +/* On Windows, NAME_MAX is not defined. */ +#ifndef NAME_MAX +#define NAME_MAX FILENAME_MAX +#endif + +int +optgroup_realpath_available (void) +{ +#ifdef HAVE_REALPATH + return 1; +#else + return 0; +#endif +} + char * -do_realpath (char *path) +do_realpath (const char *path) { +#ifdef HAVE_REALPATH char *ret; - NEED_ROOT (NULL); - ABS_PATH (path, NULL); - CHROOT_IN; ret = realpath (path, NULL); CHROOT_OUT; if (ret == NULL) { - reply_with_perror ("realpath"); + reply_with_perror ("%s", path); return NULL; } return ret; /* caller frees */ +#else + NOT_AVAILABLE (NULL); +#endif +} + +char * +do_case_sensitive_path (const char *path) +{ + char ret[PATH_MAX+1] = "/"; + size_t next = 1; + int fd_cwd; + + /* 'fd_cwd' here is a surrogate for the current working directory, so + * that we don't have to actually call chdir(2). + */ + fd_cwd = open (sysroot, O_RDONLY | O_DIRECTORY); + if (fd_cwd == -1) { + reply_with_perror ("%s", sysroot); + return NULL; + } + + /* First character is a '/'. Take each subsequent path element + * and follow it. + */ + while (*path) { + size_t i = strcspn (path, "/"); + if (i == 0) { + path++; + continue; + } + + if (verbose) + fprintf (stderr, "case_sensitive_path: path = %s, next = %zu, i = %zu\n", + path, next, i); + + if ((i == 1 && path[0] == '.') || + (i == 2 && path[0] == '.' && path[1] == '.')) { + reply_with_error ("path contained . or .. elements"); + goto error; + } + if (i > NAME_MAX) { + reply_with_error ("path element too long"); + goto error; + } + + char name[NAME_MAX+1]; + memcpy (name, path, i); + name[i] = '\0'; + + /* Skip to next element in path (for the next loop iteration). */ + path += i; + + /* Read the current directory looking (case insensitively) for + * this element of the path. + */ + int fd2 = dup (fd_cwd); /* because closedir will close it */ + if (fd2 == -1) { + reply_with_perror ("dup"); + goto error; + } + DIR *dir = fdopendir (fd2); + if (dir == NULL) { + reply_with_perror ("opendir"); + goto error; + } + + struct dirent *d = NULL; + + errno = 0; + while ((d = readdir (dir)) != NULL) { + if (STRCASEEQ (d->d_name, name)) + break; + } + + if (d == NULL && errno != 0) { + reply_with_perror ("readdir"); + goto error; + } + + if (closedir (dir) == -1) { + reply_with_perror ("closedir"); + goto error; + } + + if (d == NULL) { + reply_with_error ("%s: no file or directory found with this name", name); + goto error; + } + + /* Add the real name of this path element to the return value. */ + if (next > 1) + ret[next++] = '/'; + + i = strlen (d->d_name); + if (next + i >= PATH_MAX) { + reply_with_error ("final path too long"); + goto error; + } + + strcpy (&ret[next], d->d_name); + next += i; + + /* Is it a directory? Try going into it. */ + fd2 = openat (fd_cwd, d->d_name, O_RDONLY | O_DIRECTORY); + int err = errno; + close (fd_cwd); + fd_cwd = fd2; + errno = err; + if (fd_cwd == -1) { + /* ENOTDIR is OK provided we've reached the end of the path. */ + if (errno != ENOTDIR) { + reply_with_perror ("openat: %s", d->d_name); + goto error; + } + + if (*path) { + reply_with_error ("%s: non-directory element in path", d->d_name); + goto error; + } + } + } + + close (fd_cwd); + + ret[next] = '\0'; + char *retp = strdup (ret); + if (retp == NULL) { + reply_with_perror ("strdup"); + return NULL; + } + return retp; /* caller frees */ + + error: + close (fd_cwd); + return NULL; }