From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 17218 invoked from network); 5 Jan 1997 00:11:04 -0000 Received: from euclid.skiles.gatech.edu (list@130.207.146.50) by coral.primenet.com.au with SMTP; 5 Jan 1997 00:11:04 -0000 Received: (from list@localhost) by euclid.skiles.gatech.edu (8.7.3/8.7.3) id TAA10213; Sat, 4 Jan 1997 19:16:19 -0500 (EST) Resent-Date: Sat, 4 Jan 1997 19:16:19 -0500 (EST) From: Zoltan Hidvegi Message-Id: <199701050012.BAA01817@hzoli.ppp.cs.elte.hu> Subject: files module: safe rm To: zsh-workers@math.gatech.edu (Zsh hacking and development) Date: Sun, 5 Jan 1997 01:12:22 +0100 (MET) X-Mailer: ELM [version 2.4ME+ PL17 (25)] MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Resent-Message-ID: <"faYvX.0.VV2.JBlpo"@euclid> Resent-From: zsh-workers@math.gatech.edu X-Mailing-List: archive/latest/2727 X-Loop: zsh-workers@math.gatech.edu Precedence: list Resent-Sender: zsh-workers-request@math.gatech.edu This patch adds the -s option to rm which will not follow symbolic links. It also adds the necessary checks after chdir(".."); For example rm -s /l/foo will fail if /l is a symlink. The -s option can also be used with cd to ignore symlinks. I tried to optimise the number of system calls in rm -s (but it's still far from optimal). Zoltan *** Src/builtin.c 1997/01/02 18:58:06 3.1.1.4 --- Src/builtin.c 1997/01/04 23:59:52 *************** *** 885,890 **** --- 885,892 ---- struct stat st1, st2; doprintdir = (doprintdir == -1); + if (**argv == '-' && (*argv)[1] == 's' && (*argv)[2] == '\0') + ops['s'] = 1, argv++; PERMALLOC { pushnode(dirstack, ztrdup(pwd)); *************** *** 985,991 **** if (!dir) { dir = firstnode(dirstack); } ! if (!(dest = cd_do_chdir(nam, getdata(dir)))) { if (!target) zsfree(getlinknode(dirstack)); if (func == BIN_POPD) --- 987,993 ---- if (!dir) { dir = firstnode(dirstack); } ! if (!(dest = cd_do_chdir(nam, getdata(dir), ops['s']))) { if (!target) zsfree(getlinknode(dirstack)); if (func == BIN_POPD) *************** *** 1007,1013 **** /**/ char * ! cd_do_chdir(char *cnam, char *dest) { char **pp, *ret; int hasdot = 0, eno = ENOENT; --- 1009,1015 ---- /**/ char * ! cd_do_chdir(char *cnam, char *dest, int hard) { char **pp, *ret; int hasdot = 0, eno = ENOENT; *************** *** 1020,1026 **** /* if we have an absolute path, use it as-is only */ if (*dest == '/') { ! if ((ret = cd_try_chdir(NULL, dest))) return ret; zwarnnam(cnam, "%e: %s", dest, errno); return NULL; --- 1022,1028 ---- /* if we have an absolute path, use it as-is only */ if (*dest == '/') { ! if ((ret = cd_try_chdir(NULL, dest, hard))) return ret; zwarnnam(cnam, "%e: %s", dest, errno); return NULL; *************** *** 1034,1040 **** /* if there is no . in cdpath (or it is not being used), try the directory as-is (i.e. from .) */ if (!hasdot) { ! if ((ret = cd_try_chdir(NULL, dest))) return ret; if (errno != ENOENT) eno = errno; --- 1036,1042 ---- /* if there is no . in cdpath (or it is not being used), try the directory as-is (i.e. from .) */ if (!hasdot) { ! if ((ret = cd_try_chdir(NULL, dest, hard))) return ret; if (errno != ENOENT) eno = errno; *************** *** 1043,1049 **** cdpath in turn */ if (!nocdpath) for (pp = cdpath; *pp; pp++) { ! if ((ret = cd_try_chdir(*pp, dest))) { if (strcmp(*pp, ".")) { doprintdir++; } --- 1045,1051 ---- cdpath in turn */ if (!nocdpath) for (pp = cdpath; *pp; pp++) { ! if ((ret = cd_try_chdir(*pp, dest, hard))) { if (strcmp(*pp, ".")) { doprintdir++; } *************** *** 1055,1061 **** /* handle the CDABLEVARS option */ if ((ret = cd_able_vars(dest))) { ! if ((ret = cd_try_chdir(NULL, ret))) { doprintdir++; return ret; } --- 1057,1063 ---- /* handle the CDABLEVARS option */ if ((ret = cd_able_vars(dest))) { ! if ((ret = cd_try_chdir(NULL, ret,hard))) { doprintdir++; return ret; } *************** *** 1103,1109 **** /**/ char * ! cd_try_chdir(char *pfix, char *dest) { char *buf; --- 1105,1111 ---- /**/ char * ! cd_try_chdir(char *pfix, char *dest, int hard) { char *buf; *************** *** 1136,1142 **** /* Normalise path. See the definition of fixdir() for what this means. */ fixdir(buf); ! if (zchdir(buf)) { zsfree(buf); return NULL; } --- 1138,1144 ---- /* Normalise path. See the definition of fixdir() for what this means. */ fixdir(buf); ! if (lchdir(buf, NULL, hard)) { zsfree(buf); return NULL; } *** Src/utils.c 1997/01/03 01:49:22 3.1.1.12 --- Src/utils.c 1997/01/04 23:59:44 *************** *** 3354,3366 **** { char buf[PATH_MAX]; char *s; while (n > 0) { for (s = buf; s < buf + PATH_MAX - 4 && n--; ) *s++ = '.', *s++ = '.', *s++ = '/'; s[-1] = '\0'; if (chdir(buf)) ! return -1; } return 0; } --- 3354,3368 ---- { char buf[PATH_MAX]; char *s; + int err = -1; while (n > 0) { for (s = buf; s < buf + PATH_MAX - 4 && n--; ) *s++ = '.', *s++ = '.', *s++ = '/'; s[-1] = '\0'; if (chdir(buf)) ! return err; ! err = -2; } return 0; } *************** *** 3372,3397 **** /**/ int ! lchdir(char const *path) { - #ifndef HAVE_LSTAT - return zchdir(path); - #else /* HAVE_LSTAT */ - char buf[PATH_MAX + 1], *ptr; char const *pptr; ! int olddir = open(".", O_RDONLY), level, err; ! struct stat st1, st2; ! if(*path == '/') { ! chdir("/"); level = -1; ! } else level = 0; for(;;) { while(*path == '/') path++; if(!*path) { ! close(olddir); return 0; } for(pptr = path; *++pptr && *pptr != '/'; ) ; --- 3374,3440 ---- /**/ int ! lchdir(char const *path, struct dirsav *d, int hard) { char const *pptr; ! int level; ! struct stat st1; ! struct dirsav ds; ! #ifdef HAVE_LSTAT ! char buf[PATH_MAX + 1], *ptr; ! int err; ! struct stat st2; ! #endif ! if (!d) { ! ds.ino = ds.dev = 0; ! ds.dirname = NULL; ! ds.dirfd = -1; ! d = &ds; ! } ! #ifdef HAVE_LSTAT ! if (*path == '/' || !hard) { ! #else ! if (*path == '/') { ! #endif level = -1; ! if (d->dirfd < 0 && (d->dirfd = open(".", O_RDONLY)) < 0 && ! zgetdir(d) && *d->dirname != '/') ! d->dirfd = open("..", O_RDONLY); ! } else { level = 0; + if (!d->dev && !d->ino) { + stat(".", &st1); + d->dev = st1.st_dev; + d->ino = st1.st_ino; + } + } + + #ifdef HAVE_LSTAT + if (!hard) + #endif + { + if (d == &ds) + zsfree(ds.dirname); + else { + for (pptr = path; *pptr; level++) { + while (*pptr && *pptr++ != '/'); + while (*pptr == '/') + pptr++; + } + d->level = level; + } + return zchdir((char *) path); + } + #ifdef HAVE_LSTAT for(;;) { while(*path == '/') path++; if(!*path) { ! if (d == &ds) ! zsfree(ds.dirname); ! else ! d->level = level; return 0; } for(pptr = path; *++pptr && *pptr != '/'; ) ; *************** *** 3425,3438 **** break; } } ! if (olddir >= 0) { ! fchdir(olddir); ! close(olddir); ! } else ! upchdir(level); errno = err; return -1; #endif /* HAVE_LSTAT */ } #ifdef DEBUG --- 3468,3518 ---- break; } } ! if (restoredir(d)) { ! if (d == &ds) ! zsfree(ds.dirname); ! errno = err; ! return -2; ! } ! if (d == &ds) ! zsfree(ds.dirname); errno = err; return -1; #endif /* HAVE_LSTAT */ + } + + /**/ + int + restoredir(struct dirsav *d) + { + int err = 0; + struct stat sbuf; + + if (d->dirname && *d->dirname == '/') + return chdir(d->dirname); + if (d->dirfd >= 0) { + if (!fchdir(d->dirfd)) { + if (!d->dirname) { + return 0; + } else if (chdir(d->dirname)) { + close(d->dirfd); + d->dirfd = -1; + err = -2; + } + } else { + close(d->dirfd); + d->dirfd = err = -1; + } + } else if (d->level > 0) + err = upchdir(d->level); + else if (d->level < 0) + err = -1; + if (d->dev || d->ino) { + stat(".", &sbuf); + if (sbuf.st_ino != d->ino || sbuf.st_dev != d->dev) + err = -2; + } + return err; } #ifdef DEBUG *** Src/Modules/files.c 1997/01/02 03:53:56 3.1.1.3 --- Src/Modules/files.c 1997/01/04 23:38:02 *************** *** 307,333 **** bin_rm(char *nam, char **args, char *ops, int func) { int err = 0, len; ! char *rp; for(; !(err & 2) && *args; args++) { rp = ztrdup(*args); unmetafy(rp, &len); ! err |= dorm(nam, *args, rp, ops, 1); zfree(rp, len + 1); } return ops['f'] ? 0 : !!err; } /**/ static int ! dorm(char *nam, char *arg, char *rp, char *ops, int first) { struct stat st; if((!ops['d'] || !ops['f']) && !lstat(rp, &st)) { if(!ops['d'] && S_ISDIR(st.st_mode)) { if(ops['r']) ! return dormr(nam, arg, rp, ops, first); if(!ops['f']) zwarnnam(nam, "%s: %e", arg, EISDIR); return 1; --- 307,378 ---- bin_rm(char *nam, char **args, char *ops, int func) { int err = 0, len; ! char *rp, *s; ! struct dirsav ds; + ds.ino = ds.dev = 0; + ds.dirname = NULL; + ds.dirfd = ds.level = -1; + if (ops['r'] || ops['s']) { + if ((ds.dirfd = open(".", O_RDONLY)) < 0 && + zgetdir(&ds) && *ds.dirname != '/') + ds.dirfd = open("..", O_RDONLY); + } for(; !(err & 2) && *args; args++) { rp = ztrdup(*args); unmetafy(rp, &len); ! if (ops['s']) { ! s = strrchr(rp, '/'); ! if (s && !s[1]) { ! while (*s == '/' && s > rp) ! *s-- = '\0'; ! while (*s != '/' && s > rp) ! s--; ! } ! if (s && s[1]) { ! int e; ! ! *s = '\0'; ! e = lchdir(s, &ds, 1); ! err |= -e; ! if (!e) { ! struct dirsav d; ! ! d.ino = d.dev = 0; ! d.dirname = NULL; ! d.dirfd = d.level = -1; ! err |= dorm(nam, *args, s + 1, ops, &d, 0); ! zsfree(d.dirname); ! if (restoredir(&ds)) ! err |= 2; ! } ! } else ! err |= dorm(nam, *args, rp, ops, &ds, 0); ! } else ! err |= dorm(nam, *args, rp, ops, &ds, 1); zfree(rp, len + 1); } + if ((err & 2) && ds.dirfd >= 0 && restoredir(&ds) && zchdir(pwd)) { + zsfree(pwd); + pwd = ztrdup("/"); + chdir(pwd); + } + if (ds.dirfd >= 0) + close(ds.dirfd); + zsfree(ds.dirname); return ops['f'] ? 0 : !!err; } /**/ static int ! dorm(char *nam, char *arg, char *rp, char *ops, struct dirsav *ds, int first) { struct stat st; if((!ops['d'] || !ops['f']) && !lstat(rp, &st)) { if(!ops['d'] && S_ISDIR(st.st_mode)) { if(ops['r']) ! return dormr(nam, arg, rp, ops, ds, first); if(!ops['f']) zwarnnam(nam, "%s: %e", arg, EISDIR); return 1; *************** *** 359,377 **** /**/ static int ! dormr(char *nam, char *arg, char *rp, char *ops, int first) { char *fn; DIR *d; ! int err = 0; ! int pwd = *rp == '/' ? open(".", O_RDONLY) : -1; ! if(first ? zchdir(rp) : lchdir(rp)) { ! if(!ops['f']) zwarnnam(nam, "%s: %e", arg, errno); ! return 1; } d = opendir("."); if(!d) { if(!ops['f']) --- 404,426 ---- /**/ static int ! dormr(char *nam, char *arg, char *rp, char *ops, struct dirsav *ds, int first) { char *fn; DIR *d; ! int err; ! struct dirsav dsav; ! err = -lchdir(rp, ds, !first); ! if (err) { ! if (!ops['f']) zwarnnam(nam, "%s: %e", arg, errno); ! return err; } + dsav.ino = dsav.dev = 0; + dsav.dirname = NULL; + dsav.dirfd = dsav.level = -1; d = opendir("."); if(!d) { if(!ops['f']) *************** *** 383,420 **** int len; fn = ztrdup(unmetafy(fn, &len)); ! err |= dorm(nam, narg, fn, ops, 0); zsfree(narg); zfree(fn, len + 1); if(err & 2) { closedir(d); ! close(pwd); return 2; } } closedir(d); } ! if (pwd < 0 || fchdir(pwd)) { ! int level = 0; ! ! if (pwd >= 0) ! close(pwd); ! if (*rp != '/') { ! for (fn = rp; *fn; level++) { ! while (*fn && *fn++ != '/'); ! while (*fn == '/') ! fn++; ! } ! level = !upchdir(level); ! } ! if (!level) { ! if(!ops['f']) ! zwarnnam(nam, "failed to return to previous directory: %e", ! NULL, errno); ! return 2; ! } ! } else ! close(pwd); if(!ops['f'] && ops['i']) { nicezputs(nam, stderr); fputs(": remove `", stderr); --- 432,455 ---- int len; fn = ztrdup(unmetafy(fn, &len)); ! err |= dorm(nam, narg, fn, ops, &dsav, 0); zsfree(narg); zfree(fn, len + 1); if(err & 2) { closedir(d); ! zsfree(dsav.dirname); return 2; } } closedir(d); } ! zsfree(dsav.dirname); ! if (restoredir(ds)) { ! if(!ops['f']) ! zwarnnam(nam, "failed to return to previous directory: %e", ! NULL, errno); ! return 2; ! } if(!ops['f'] && ops['i']) { nicezputs(nam, stderr); fputs(": remove `", stderr); *************** *** 435,449 **** static struct binlist bintab[] = { #ifdef HAVE_LSTAT ! { "ln", 0, bin_ln, 1, -1, BIN_LN, "dfis", NULL, 0 }, #else ! { "ln", 0, bin_ln, 1, -1, BIN_LN, "dfi", NULL, 0 }, #endif ! { "mkdir", 0, bin_mkdir, 1, -1, 0, "pm", NULL, 0 }, ! { "mv", 0, bin_ln, 2, -1, BIN_MV, "fi", NULL, 0 }, ! { "rm", 0, bin_rm, 1, -1, 0, "dfir", NULL, 0 }, ! { "rmdir", 0, bin_rmdir, 1, -1, 0, NULL, NULL, 0 }, ! { "sync", 0, bin_sync, 0, 0, 0, NULL, NULL, 0 }, }; /**/ --- 470,484 ---- static struct binlist bintab[] = { #ifdef HAVE_LSTAT ! { "ln", 0, bin_ln, 1, -1, BIN_LN, "dfis", NULL, 0 }, #else ! { "ln", 0, bin_ln, 1, -1, BIN_LN, "dfi", NULL, 0 }, #endif ! { "mkdir", 0, bin_mkdir, 1, -1, 0, "pm", NULL, 0 }, ! { "mv", 0, bin_ln, 2, -1, BIN_MV, "fi", NULL, 0 }, ! { "rm", 0, bin_rm, 1, -1, 0, "dfirs", NULL, 0 }, ! { "rmdir", 0, bin_rmdir, 1, -1, 0, NULL, NULL, 0 }, ! { "sync", 0, bin_sync, 0, 0, 0, NULL, NULL, 0 }, }; /**/