[hackers] [PATCH] [sbase] ls: allow listing contents of directories with +r-x

From: David Phillips <david_AT_sighup.nz>
Date: Sun, 8 Jul 2018 22:43:04 +1200

chdir()ing into a directory with +r-x fails, so we should manually use the
directory name as a prefix rather than chdir()ing into it.

Also adds new parameters to mkent for the prefix, and for dictating whether
or not a permission denied error when stat()ing shall be fatal or not. This
allows errors stat()ing children of `foo` to be reported and non-fatal, while
a permission denied error on `foo` itself would be fatal.

Also adds a valid flag to ent to hide stale/default stat so that unknown
permissions and user/groups can be displayed as such.
---
 ls.c | 89 +++++++++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 61 insertions(+), 28 deletions(-)
diff --git a/ls.c b/ls.c
index b716aba..076f6b7 100644
--- a/ls.c
+++ b/ls.c
_AT_@ -10,11 +10,13 @@
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
+#include <errno.h>
 
 #include "utf.h"
 #include "util.h"
 
 struct entry {
+	char    valid;
 	char   *name;
 	mode_t  mode, tmode;
 	nlink_t nlink;
_AT_@ -58,15 +60,33 @@ static int showdirs;
 static void ls(const char *, const struct entry *, int);
 
 static void
-mkent(struct entry *ent, char *path, int dostat, int follow)
+mkent(struct entry *ent, char *prefix, char *name, int dostat, int follow,
+      int fatal)
 {
 	struct stat st;
+	char path[PATH_MAX];
 
-	ent->name = path;
+	ent->valid = 0;
+	ent->name = name;
 	if (!dostat)
 		return;
-	if ((follow ? stat : lstat)(path, &st) < 0)
-		eprintf("%s %s:", follow ? "stat" : "lstat", path);
+
+	if (prefix != NULL)
+		snprintf(path, PATH_MAX, "%s/%s", prefix, name);
+	else
+		strncpy(path, name, PATH_MAX);
+
+	if ((follow ? stat : lstat)(path, &st) < 0) {
+		if (errno == EACCES && !fatal) {
+			weprintf("%s %s:", follow ? "stat" : "lstat", path);
+			return;
+		} else {
+			eprintf("%s %s:", follow ? "stat" : "lstat", path);
+		}
+	} else {
+		ent->valid = 1;
+	}
+
 	ent->mode  = st.st_mode;
 	ent->nlink = st.st_nlink;
 	ent->uid   = st.st_uid;
_AT_@ -130,12 +150,13 @@ printname(const char *name)
 }
 
 static void
-output(const struct entry *ent)
+output(const char *prefix, const struct entry *ent)
 {
 	struct group *gr;
 	struct passwd *pw;
 	struct tm *tm;
 	ssize_t len;
+	char path[PATH_MAX];
 	char *fmt, buf[BUFSIZ], pwname[_SC_LOGIN_NAME_MAX],
 	     grname[_SC_LOGIN_NAME_MAX], mode[] = "----------";
 
_AT_@ -163,6 +184,14 @@ output(const struct entry *ent)
 	else
 		mode[0] = '?';
 
+	if (!ent->valid) {
+		printf("%c????????? ? ? ? ? ? ", mode[0]);
+		printname(ent->name);
+		fputs(indicator(ent->mode), stdout);
+		putchar('\n');
+		return;
+	}
+
 	if (ent->mode & S_IRUSR) mode[1] = 'r';
 	if (ent->mode & S_IWUSR) mode[2] = 'w';
 	if (ent->mode & S_IXUSR) mode[3] = 'x';
_AT_@ -208,7 +237,8 @@ output(const struct entry *ent)
 	printname(ent->name);
 	fputs(indicator(ent->mode), stdout);
 	if (S_ISLNK(ent->mode)) {
-		if ((len = readlink(ent->name, buf, sizeof(buf) - 1)) < 0)
+		snprintf(path, PATH_MAX, "%s/%s", prefix, ent->name);
+		if ((len = readlink(path, buf, sizeof(buf) - 1)) < 0)
 			eprintf("readlink %s:", ent->name);
 		buf[len] = '\0';
 		printf(" -> %s%s", buf, indicator(ent->tmode));
_AT_@ -247,13 +277,15 @@ lsdir(const char *path, const struct entry *dir)
 	size_t i, n = 0;
 	char prefix[PATH_MAX];
 
-	if (!(dp = opendir(dir->name))) {
+	if (snprintf(prefix, PATH_MAX, "%s%s", path, dir->name) >=
+		    PATH_MAX)
+			eprintf("path too long: %s%s\n", path, dir->name);
+
+	if (!(dp = opendir(prefix))) {
 		ret = 1;
-		weprintf("opendir %s%s:", path, dir->name);
+		weprintf("opendir %s:", prefix);
 		return;
 	}
-	if (chdir(dir->name) < 0)
-		eprintf("chdir %s:", dir->name);
 
 	while ((d = readdir(dp))) {
 		if (d->d_name[0] == '.' && !aflag && !Aflag)
_AT_@ -264,8 +296,12 @@ lsdir(const char *path, const struct entry *dir)
 				continue;
 
 		ents = ereallocarray(ents, ++n, sizeof(*ents));
-		mkent(&ents[n - 1], estrdup(d->d_name), Fflag || iflag ||
-		    lflag || pflag || Rflag || sort, Lflag);
+		ent = &ents[n - 1];
+		mkent(ent, prefix, estrdup(d->d_name), Fflag || iflag ||
+		    lflag || pflag || Rflag || sort, Lflag, 0);
+		if (!ent->valid) {
+			ent->mode = DTTOIF(d->d_type);
+		}
 	}
 
 	closedir(dp);
_AT_@ -279,13 +315,9 @@ lsdir(const char *path, const struct entry *dir)
 		puts(":");
 	}
 	for (i = 0; i < n; i++)
-		output(&ents[i]);
+		output(dir->name, &ents[i]);
 
 	if (Rflag) {
-		if (snprintf(prefix, PATH_MAX, "%s%s/", path, dir->name) >=
-		    PATH_MAX)
-			eprintf("path too long: %s%s\n", path, dir->name);
-
 		for (i = 0; i < n; i++) {
 			ent = &ents[i];
 			if (strcmp(ent->name, ".") == 0 ||
_AT_@ -328,10 +360,10 @@ static void
 ls(const char *path, const struct entry *ent, int listdir)
 {
 	int treeind;
-	char cwd[PATH_MAX];
+	char correct_path[PATH_MAX];
 
 	if (!listdir) {
-		output(ent);
+		output(path, ent);
 	} else if (S_ISDIR(ent->mode) ||
 	    (S_ISLNK(ent->mode) && S_ISDIR(ent->tmode))) {
 		if ((treeind = visit(ent)) < 0) {
_AT_@ -340,19 +372,20 @@ ls(const char *path, const struct entry *ent, int listdir)
 			return;
 		}
 
-		if (!getcwd(cwd, PATH_MAX))
-			eprintf("getcwd:");
-
 		if (first)
 			first = 0;
 		else
 			putchar('\n');
 
-		lsdir(path, ent);
-		tree[treeind].ino = 0;
 
-		if (chdir(cwd) < 0)
-			eprintf("chdir %s:", cwd);
+		if (strlen(path) == 0 || path[strlen(path) - 1] == '/') {
+			lsdir(path, ent);
+		} else {
+			snprintf(correct_path, sizeof(correct_path), "%s/", path);
+			lsdir(correct_path, ent);
+		}
+
+		tree[treeind].ino = 0;
 	}
 }
 
_AT_@ -446,7 +479,7 @@ main(int argc, char *argv[])
 	case 0: /* fallthrough */
 		*--argv = ".", ++argc;
 	case 1:
-		mkent(&ent, argv[0], 1, Hflag || Lflag);
+		mkent(&ent, NULL, argv[0], 1, Hflag || Lflag, 1);
 		ls("", &ent, (!dflag && S_ISDIR(ent.mode)) ||
 		    (S_ISLNK(ent.mode) && S_ISDIR(ent.tmode) &&
 		     !(dflag || Fflag || lflag)));
_AT_@ -454,7 +487,7 @@ main(int argc, char *argv[])
 		break;
 	default:
 		for (i = ds = fs = 0, fents = dents = NULL; i < argc; ++i) {
-			mkent(&ent, argv[i], 1, Hflag || Lflag);
+			mkent(&ent, NULL, argv[i], 1, Hflag || Lflag, 1);
 
 			if ((!dflag && S_ISDIR(ent.mode)) ||
 			    (S_ISLNK(ent.mode) && S_ISDIR(ent.tmode) &&
-- 
2.17.1
Received on Sun Jul 08 2018 - 12:43:04 CEST

This archive was generated by hypermail 2.3.0 : Sun Jul 08 2018 - 12:48:25 CEST