Skip to content

Commit 0b00ea7

Browse files
committed
Change xabspath() to more granular (flag based) control interface.
1 parent ffe9824 commit 0b00ea7

File tree

6 files changed

+72
-70
lines changed

6 files changed

+72
-70
lines changed

lib/lib.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,7 +1068,7 @@ char *getbasename(char *name)
10681068
// Return pointer to xabspath(file) if file is under dir, else 0
10691069
char *fileunderdir(char *file, char *dir)
10701070
{
1071-
char *s1 = xabspath(dir, 1), *s2 = xabspath(file, -1), *ss = s2;
1071+
char *s1 = xabspath(dir, ABS_FILE), *s2 = xabspath(file, 0), *ss = s2;
10721072
int rc = s1 && s2 && strstart(&ss, s1) && (!s1[1] || s2[strlen(s1)] == '/');
10731073

10741074
free(s1);
@@ -1083,8 +1083,8 @@ char *relative_path(char *from, char *to)
10831083
char *s, *ret = 0;
10841084
int i, j, k;
10851085

1086-
if (!(from = xabspath(from, -1))) return 0;
1087-
if (!(to = xabspath(to, -1))) goto error;
1086+
if (!(from = xabspath(from, 0))) return 0;
1087+
if (!(to = xabspath(to, 0))) goto error;
10881088

10891089
// skip common directories from root
10901090
for (i = j = 0; from[i] && from[i] == to[i]; i++) if (to[i] == '/') j = i+1;

lib/lib.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,7 @@ struct dirtree {
9090
char *symlink;
9191
int dirfd;
9292
struct stat st;
93-
char again;
94-
char name[];
93+
char again, name[];
9594
};
9695

9796
int isdotdot(char *name);
@@ -115,6 +114,12 @@ void show_help(FILE *out, int full);
115114
#define WARN_ONLY (1<<31) // don't exit, just warn
116115
#define LOOPFILES_ANYWAY (1<<30) // call function with fd -1
117116

117+
// xabspath flags
118+
#define ABS_PATH 1 // all but last path component must exist
119+
#define ABS_FILE 2 // last path component must exist
120+
#define ABS_KEEP 4 // don't resolve symlinks in path to last component
121+
#define ABS_LAST 8 // don't resolve symlink in last path component
122+
118123
// xwrap.c
119124
void xstrncpy(char *dest, char *src, size_t size);
120125
void xstrncat(char *dest, char *src, size_t size);

lib/xwrap.c

Lines changed: 53 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -548,88 +548,87 @@ void xstat(char *path, struct stat *st)
548548
}
549549

550550
// Canonicalize path, even to file with one or more missing components at end.
551-
// Returns allocated string for pathname or NULL if doesn't exist
552-
// exact = 1 file must exist, 0 dir must exist, -1 show theoretical location,
553-
// -2 don't resolve last file
554-
char *xabspath(char *path, int exact)
551+
// Returns allocated string for pathname or NULL if doesn't exist. Flags are:
552+
// ABS_PATH:path to last component must exist ABS_FILE: whole path must exist
553+
// ABS_KEEP:keep symlinks in path ABS_LAST: keep symlink at end of path
554+
char *xabspath(char *path, int flags)
555555
{
556-
struct string_list *todo, *done = 0;
557-
int try = 9999, dirfd = open("/", O_PATH), missing = 0;
558-
char *ret;
556+
struct string_list *todo, *done = 0, *new, **tail;
557+
int fd, track, len, try = 9999, dirfd = -1, missing = 0;
558+
char *str;
559559

560-
// If this isn't an absolute path, start with cwd.
561-
if (*path != '/') {
562-
char *temp = xgetcwd();
560+
// If the last file must exist, path to it must exist.
561+
if (flags&ABS_FILE) flags |= ABS_PATH;
562+
// If we don't resolve path's symlinks, don't resolve last symlink.
563+
if (flags&ABS_KEEP) flags |= ABS_LAST;
563564

564-
splitpath(path, splitpath(temp, &todo));
565-
free(temp);
565+
// If this isn't an absolute path, start with cwd or $PWD.
566+
if (*path != '/') {
567+
if ((flags & ABS_KEEP) && (str = getenv("PWD")))
568+
splitpath(path, splitpath(str, &todo));
569+
else {
570+
splitpath(path, splitpath(str = xgetcwd(), &todo));
571+
free(str);
572+
}
566573
} else splitpath(path, &todo);
567574

568575
// Iterate through path components in todo, prepend processed ones to done.
569576
while (todo) {
570-
struct string_list *new = llist_pop(&todo), **tail;
571-
ssize_t len;
572-
573-
// Eventually break out of endless loops
577+
// break out of endless symlink loops
574578
if (!try--) {
575579
errno = ELOOP;
576580
goto error;
577581
}
578582

579-
// Removable path componenents.
580-
if (!strcmp(new->str, ".") || !strcmp(new->str, "..")) {
581-
int x = new->str[1];
582-
583+
// Remove . or .. component, tracking dirfd back up tree as necessary
584+
str = (new = llist_pop(&todo))->str;
585+
// track dirfd if this component must exist or we're resolving symlinks
586+
track = ((flags>>!todo) & (ABS_PATH|ABS_KEEP)) ^ ABS_KEEP;
587+
if (!done && track) dirfd = open("/", O_PATH);
588+
if (*str=='.' && !str[1+((fd = str[1])=='.')]) {
583589
free(new);
584-
if (!x) continue;
585-
if (done) free(llist_pop(&done));
586-
len = 0;
587-
588-
if (missing) missing--;
589-
else {
590-
if (-1 == (x = openat(dirfd, "..", O_PATH))) goto error;
591-
close(dirfd);
592-
dirfd = x;
590+
if (fd) {
591+
if (done) free(llist_pop(&done));
592+
if (missing) missing--;
593+
else if (track) {
594+
if (-1 == (fd = openat(dirfd, "..", O_PATH))) goto error;
595+
close(dirfd);
596+
dirfd = fd;
597+
}
593598
}
594599
continue;
595600
}
596601

597602
// Is this a symlink?
598-
if (exact == -2 && !todo) len = 0;
603+
if (flags & (ABS_KEEP<<!todo)) errno = len = 0;
599604
else len = readlinkat(dirfd, new->str, libbuf, sizeof(libbuf));
600605
if (len>4095) goto error;
601606

602607
// Not a symlink: add to linked list, move dirfd, fail if error
603608
if (len<1) {
604-
int fd;
605-
606609
new->next = done;
607610
done = new;
608-
if (errno == EINVAL && !todo) break;
609-
if (errno == ENOENT && exact<0) {
610-
missing++;
611-
continue;
611+
if (errno == ENOENT && !(flags & (ABS_PATH<<!todo))) missing++;
612+
else if (errno != EINVAL && (flags & (ABS_PATH<<!todo))) goto error;
613+
else if (track) {
614+
if (-1 == (fd = openat(dirfd, new->str, O_PATH))) goto error;
615+
close(dirfd);
616+
dirfd = fd;
612617
}
613-
if (errno != EINVAL && (exact || todo)) goto error;
614-
615-
fd = openat(dirfd, new->str, O_PATH);
616-
if (fd == -1 && (exact || todo || errno != ENOENT)) goto error;
617-
close(dirfd);
618-
dirfd = fd;
619618
continue;
620619
}
621620

622621
// If this symlink is to an absolute path, discard existing resolved path
623622
libbuf[len] = 0;
624623
if (*libbuf == '/') {
625624
llist_traverse(done, free);
626-
done=0;
625+
done = 0;
627626
close(dirfd);
628-
dirfd = open("/", O_PATH);
627+
dirfd = -1;
629628
}
630629
free(new);
631630

632-
// prepend components of new path. Note symlink to "/" will leave new NULL
631+
// prepend components of new path. Note symlink to "/" will leave new = NULL
633632
tail = splitpath(libbuf, &new);
634633

635634
// symlink to "/" will return null and leave tail alone
@@ -638,11 +637,10 @@ char *xabspath(char *path, int exact)
638637
todo = new;
639638
}
640639
}
641-
close(dirfd);
642-
643-
// At this point done has the path, in reverse order. Reverse list while
644-
// calculating buffer length.
640+
xclose(dirfd);
645641

642+
// At this point done has the path, in reverse order. Reverse list
643+
// (into todo) while calculating buffer length.
646644
try = 2;
647645
while (done) {
648646
struct string_list *temp = llist_pop(&done);
@@ -654,20 +652,18 @@ char *xabspath(char *path, int exact)
654652
}
655653

656654
// Assemble return buffer
657-
658-
ret = xmalloc(try);
659-
*ret = '/';
660-
ret [try = 1] = 0;
655+
*(str = xmalloc(try)) = '/';
656+
str[try = 1] = 0;
661657
while (todo) {
662-
if (try>1) ret[try++] = '/';
663-
try = stpcpy(ret+try, todo->str) - ret;
658+
if (try>1) str[try++] = '/';
659+
try = stpcpy(str+try, todo->str) - str;
664660
free(llist_pop(&todo));
665661
}
666662

667-
return ret;
663+
return str;
668664

669665
error:
670-
close(dirfd);
666+
xclose(dirfd);
671667
llist_traverse(todo, free);
672668
llist_traverse(done, free);
673669

toys/other/losetup.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,9 @@ static int loopback_setup(char *device, char *file)
103103
}
104104
// Associate file with this device?
105105
} else if (file) {
106-
char *f_path = xabspath(file, 1);
106+
char *f_path = xabspath(file, ABS_PATH);
107107

108-
if (!f_path) perror_exit("file"); // already opened, but if deleted since...
108+
if (!f_path) perror_exit("%s", file); // already opened but if deleted since
109109
if (ioctl(lfd, LOOP_SET_FD, ffd)) {
110110
free(f_path);
111111
if (racy && errno == EBUSY) return 1;

toys/other/readlink.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
*
33
* Copyright 2007 Rob Landley <rob@landley.net>
44
5+
// -ef positions match ABS_FILE ABS_PATH
56
USE_READLINK(NEWTOY(readlink, "<1nqmef(canonicalize)[-mef]", TOYFLAG_USR|TOYFLAG_BIN))
67
USE_REALPATH(NEWTOY(realpath, "<1", TOYFLAG_USR|TOYFLAG_BIN))
78
@@ -40,14 +41,14 @@ void readlink_main(void)
4041

4142
for (arg = toys.optargs; *arg; arg++) {
4243
// Calculating full canonical path?
43-
// Take advantage of flag positions to calculate m = -1, f = 0, e = 1
44+
// Take advantage of flag positions: m = 0, f = ABS_PATH, e = ABS_FILE
4445
if (toys.optflags & (FLAG_f|FLAG_e|FLAG_m))
45-
s = xabspath(*arg, (toys.optflags&(FLAG_f|FLAG_e))-1);
46+
s = xabspath(*arg, toys.optflags&(FLAG_f|FLAG_e));
4647
else s = xreadlink(*arg);
4748

4849
if (s) {
49-
if (!FLAG(q)) xprintf(FLAG(n) ? "%s" : "%s\n", s);
50-
if (CFG_TOYBOX_FREE) free(s);
50+
if (!FLAG(q)) xprintf("%s%s", s, (FLAG(n) && !arg[1]) ? "" : "\n");
51+
free(s);
5152
} else toys.exitval = 1;
5253
}
5354
}

toys/posix/tar.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ static int dirflush(char *name, int isdir)
399399

400400
// Barf if name not in TT.cwd
401401
if (name) {
402-
if (!(ss = s = xabspath(name, -1-isdir))) {
402+
if (!(ss = s = xabspath(name, isdir ? ABS_LAST : 0))) {
403403
error_msg("'%s' bad symlink", name);
404404

405405
return 1;
@@ -834,7 +834,7 @@ void tar_main(void)
834834
}
835835

836836
// Get destination directory
837-
TT.cwd = xabspath(s = xgetcwd(), 1);
837+
TT.cwd = xabspath(s = xgetcwd(), ABS_PATH);
838838
free(s);
839839

840840
// Remember archive inode so we don't overwrite it or add it to itself

0 commit comments

Comments
 (0)