Richard W.M. Jones wrote:
...
diff --git a/daemon/realpath.c b/daemon/realpath.c
...
Hi Rich,
@@ -42,3 +46,111 @@ do_realpath (const char *path)
return ret; /* caller frees */
}
+
+char *
+do_case_sensitive_path (const char *path)
+{
+ char ret[PATH_MAX+1] = "/";
+ size_t next = 1;
+
+ /* MUST chdir ("/") before leaving this function. */
+ if (chdir (sysroot) == -1) {
If this function might ever be used from a multi-threaded
application, then you'll want to change it not to use chdir,
since chdir changes the process-wide current directory.
That can cause rare but particularly hard to debug problems.
Another (albeit small) advantage of not performing the chdir/readdir is
that the function will be able to handle those rare names
with a directory component that grants "x" but not "r" access.
+ reply_with_perror ("%s", sysroot);
+ return NULL;
+ }
+
+ /* First character is a '/'. Take each subsequent path element
+ * and follow it.
+ */
+ path++;
+ while (*path) {
+ size_t i = strcspn (path, "/");
+ if (i == 0) { /* "//" in path */
+ path++;
+ continue;
+ }
+ if ((i == 1 && path[0] == '.') ||
+ (i == 2 && path[0] == '.' && path[1] == '.')) {
+ reply_with_error ("case_sensitive_path: path contained . or ..
elements");
+ goto error;
+ }
+ if (i > NAME_MAX) {
+ reply_with_error ("case_sensitive_path: 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;
+ if (*path == '/') path++;
+
+ /* Read the current directory looking (case insensitively) for
+ * this element of the path.
+ */
+ DIR *dir = opendir (".");
+ if (dir == NULL) {
+ reply_with_perror ("opendir");
+ goto error;
+ }
+
+ struct dirent *d = NULL;
+
+ while ((d = readdir (dir)) != NULL) {
If you want to distinguish readdir "EOF" from failure (e.g., EIO),
you'll have to set errno=0 before each call and test whether it's
still 0 upon NULL return.
+ if (strcasecmp (d->d_name, name) == 0)
+ break;
+ }
+
+ 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. */
+ if (chdir (d->d_name) == 0)
+ continue;
+
+ /* This is OK provided we've reached the end of the path. */
+ if (errno == ENOTDIR) {
+ if (*path) {
+ reply_with_error ("non-directory element in path");
+ goto error;
+ }
+ break;
+ }
What about other errno values?
+ }
+
+ ignore_value (chdir ("/"));
+
+ ret[next] = '\0';
+ char *retp = strdup (ret);
+ if (retp == NULL) {
+ reply_with_perror ("strdup");
+ return NULL;
+ }
+ return retp; /* caller frees */
+
+ error:
+ ignore_value (chdir ("/"));
+ return NULL;