about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTanaka Akira <akr@users.sourceforge.net>1999-12-09 16:09:31 +0000
committerTanaka Akira <akr@users.sourceforge.net>1999-12-09 16:09:31 +0000
commit4f9b1b9804b2427ebe99cd8f79ff17fd22570957 (patch)
tree20ccd1815c022adc18172fd7ded79956b84d6764
parent015e22c1411caf67a4026397f89652c568c02bcc (diff)
downloadzsh-4f9b1b9804b2427ebe99cd8f79ff17fd22570957.tar.gz
zsh-4f9b1b9804b2427ebe99cd8f79ff17fd22570957.tar.xz
zsh-4f9b1b9804b2427ebe99cd8f79ff17fd22570957.zip
zsh-workers/8982
-rw-r--r--Doc/Zsh/mod_files.yo37
-rw-r--r--Src/Modules/files.c336
-rw-r--r--Src/Modules/files.mdd2
-rw-r--r--Src/system.h4
-rw-r--r--configure.in2
5 files changed, 311 insertions, 70 deletions
diff --git a/Doc/Zsh/mod_files.yo b/Doc/Zsh/mod_files.yo
index 932d8583c..356696faa 100644
--- a/Doc/Zsh/mod_files.yo
+++ b/Doc/Zsh/mod_files.yo
@@ -4,6 +4,43 @@ cindex(files, manipulating)
 The tt(files) module makes some standard commands available as builtins:
 
 startitem()
+findex(chgrp)
+item(tt(chgrp) [ tt(-Rs) ] var(group) var(filename) ...)(
+Changes group of files specified.  This is equivalent to tt(chown) with
+a var(user-spec) argument of `tt(:)var(group)'.
+)
+findex(chown)
+item(tt(chown) [ tt(-Rs) ] var(user-spec) var(filename) ...)(
+Changes ownership and group of files specified.
+
+The var(user-spec) can be in four forms:
+
+startsitem()
+sitem(var(user))(change owner to var(user); do not change group)
+sitem(var(user)tt(:))(change owner to var(user); change group to var(user)'s primary group)
+sitem(var(user)tt(:)var(group))(change owner to var(user); change group to var(group))
+sitem(tt(:)var(group))(do not change owner; change group to var(group))
+endsitem()
+
+In each case, the `tt(:)' may instead be a `tt(.)'.
+Each of var(user) and var(group) may be either a username (or group name, as
+appropriate) or a decimal user ID (group ID).  Interpretation as a name
+takes precedence, if there is an all-numeric username (or group name).
+
+The tt(-R) option causes tt(chown) to recursively descend into directories,
+changing the ownership of all files in the directory after
+changing the ownership of the directory itself.
+
+The tt(-s) option is a zsh extension to tt(chown) functionality.  It enables
+paranoid behaviour, intended to avoid security problems involving
+a tt(chown) being tricked into affecting files other than the ones
+intended.  It will refuse to follow symbolic links, so that (for example)
+``tt(chown luser /tmp/foo/passwd)'' can't accidentally chown tt(/etc/passwd)
+if tt(/tmp/foo) happens to be a link to tt(/etc).  It will also check
+where it is after leaving directories, so that a recursive chown of
+a deep directory tree can't end up recursively chowning tt(/usr) as
+a result of directories being moved up the tree.
+)
 findex(ln)
 xitem(tt(ln) [ tt(-dfis) ] var(filename) var(dest))
 item(tt(ln) [ tt(-dfis) ] var(filename) ... var(dir))(
diff --git a/Src/Modules/files.c b/Src/Modules/files.c
index dc91227f8..844ca7d25 100644
--- a/Src/Modules/files.c
+++ b/Src/Modules/files.c
@@ -30,6 +30,7 @@
 #include "files.mdh"
 
 typedef int (*MoveFunc) _((char const *, char const *));
+typedef int (*RecurseFunc) _((char *, char *, struct stat const *, void *));
 
 #ifndef STDC_HEADERS
 extern int link _((const char *, const char *));
@@ -37,6 +38,8 @@ extern int symlink _((const char *, const char *));
 extern int rename _((const char *, const char *));
 #endif
 
+struct recursivecmd;
+
 #include "files.pro"
 
 /**/
@@ -312,20 +315,42 @@ domove(char *nam, MoveFunc move, char *p, char *q, int flags)
     return 0;
 }
 
-/* rm builtin */
+/* general recursion */
+
+struct recursivecmd {
+    char *nam;
+    int opt_noerr;
+    int opt_recurse;
+    int opt_safe;
+    RecurseFunc dirpre_func;
+    RecurseFunc dirpost_func;
+    RecurseFunc leaf_func;
+    void *magic;
+};
 
 /**/
 static int
-bin_rm(char *nam, char **args, char *ops, int func)
+recursivecmd(char *nam, int opt_noerr, int opt_recurse, int opt_safe,
+    char **args, RecurseFunc dirpre_func, RecurseFunc dirpost_func,
+    RecurseFunc leaf_func, void *magic)
 {
     int err = 0, len;
     char *rp, *s;
     struct dirsav ds;
-
+    struct recursivecmd reccmd;
+
+    reccmd.nam = nam;
+    reccmd.opt_noerr = opt_noerr;
+    reccmd.opt_recurse = opt_recurse;
+    reccmd.opt_safe = opt_safe;
+    reccmd.dirpre_func = dirpre_func;
+    reccmd.dirpost_func = dirpost_func;
+    reccmd.leaf_func = leaf_func;
+    reccmd.magic = magic;
     ds.ino = ds.dev = 0;
     ds.dirname = NULL;
     ds.dirfd = ds.level = -1;
-    if (ops['r'] || ops['s']) {
+    if (opt_recurse || opt_safe) {
 	if ((ds.dirfd = open(".", O_RDONLY|O_NOCTTY)) < 0 &&
 	    zgetdir(&ds) && *ds.dirname != '/')
 	    ds.dirfd = open("..", O_RDONLY|O_NOCTTY);
@@ -333,7 +358,7 @@ bin_rm(char *nam, char **args, char *ops, int func)
     for(; !errflag && !(err & 2) && *args; args++) {
 	rp = ztrdup(*args);
 	unmetafy(rp, &len);
-	if (ops['s']) {
+	if (opt_safe) {
 	    s = strrchr(rp, '/');
 	    if (s && !s[1]) {
 		while (*s == '/' && s > rp)
@@ -353,16 +378,16 @@ bin_rm(char *nam, char **args, char *ops, int func)
 		    d.ino = d.dev = 0;
 		    d.dirname = NULL;
 		    d.dirfd = d.level = -1;
-		    err |= dorm(nam, *args, s + 1, ops, &d, 0);
+		    err |= recursivecmd_doone(&reccmd, *args, s + 1, &d, 0);
 		    zsfree(d.dirname);
 		    if (restoredir(&ds))
 			err |= 2;
-		} else
+		} else if(!opt_noerr)
 		    zwarnnam(nam, "%s: %e", *args, errno);
 	    } else
-		err |= dorm(nam, *args, rp, ops, &ds, 0);
+		err |= recursivecmd_doone(&reccmd, *args, rp, &ds, 0);
 	} else
-	    err |= dorm(nam, *args, rp, ops, &ds, 1);
+	    err |= recursivecmd_doone(&reccmd, *args, rp, &ds, 1);
 	zfree(rp, len + 1);
     }
     if ((err & 2) && ds.dirfd >= 0 && restoredir(&ds) && zchdir(pwd)) {
@@ -373,76 +398,55 @@ bin_rm(char *nam, char **args, char *ops, int func)
     if (ds.dirfd >= 0)
 	close(ds.dirfd);
     zsfree(ds.dirname);
-    return ops['f'] ? 0 : !!err;
+    return !!err;
 }
 
 /**/
 static int
-dorm(char *nam, char *arg, char *rp, char *ops, struct dirsav *ds, int first)
+recursivecmd_doone(struct recursivecmd const *reccmd,
+    char *arg, char *rp, struct dirsav *ds, int first)
 {
-    struct stat st;
+    struct stat st, *sp = NULL;
 
-    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;
-	}
-	if(!ops['f'] && ops['i']) {
-	    nicezputs(nam, stderr);
-	    fputs(": remove `", stderr);
-	    nicezputs(arg, stderr);
-	    fputs("'? ", stderr);
-	    fflush(stderr);
-	    if(!ask())
-		return 0;
-	} else if(!ops['f'] &&
-		!S_ISLNK(st.st_mode) &&
-	    	access(rp, W_OK)) {
-	    nicezputs(nam, stderr);
-	    fputs(": remove `", stderr);
-	    nicezputs(arg, stderr);
-	    fprintf(stderr, "', overriding mode %04o? ",
-		mode_to_octal(st.st_mode));
-	    fflush(stderr);
-	    if(!ask())
-		return 0;
-	}
+    if(reccmd->opt_recurse && !lstat(rp, &st)) {
+	if(S_ISDIR(st.st_mode))
+	    return recursivecmd_dorec(reccmd, arg, rp, &st, ds, first);
+	sp = &st;
     }
-    if(!unlink(rp))
-	return 0;
-    if(!ops['f'])
-	zwarnnam(nam, "%s: %e", arg, errno);
-    return 1;
+    return reccmd->leaf_func(arg, rp, sp, reccmd->magic);
 }
 
 /**/
 static int
-dormr(char *nam, char *arg, char *rp, char *ops, struct dirsav *ds, int first)
+recursivecmd_dorec(struct recursivecmd const *reccmd,
+    char *arg, char *rp, struct stat const *sp, struct dirsav *ds, int first)
 {
     char *fn;
     DIR *d;
-    int err;
+    int err, err1;
     struct dirsav dsav;
     char *files = NULL;
     int fileslen = 0;
 
+    err1 = reccmd->dirpre_func(arg, rp, sp, reccmd->magic);
+    if(err1 & 2)
+	return 2;
+
     err = -lchdir(rp, ds, !first);
     if (err) {
-	if (!ops['f'])
-	    zwarnnam(nam, "%s: %e", arg, errno);
+	if(!reccmd->opt_noerr)
+	    zwarnnam(reccmd->nam, "%s: %e", arg, errno);
 	return err;
     }
+    err = err1;
 
     dsav.ino = dsav.dev = 0;
     dsav.dirname = NULL;
     dsav.dirfd = dsav.level = -1;
     d = opendir(".");
     if(!d) {
-	if(!ops['f'])
-	    zwarnnam(nam, "%s: %e", arg, errno);
+	if(!reccmd->opt_noerr)
+	    zwarnnam(reccmd->nam, "%s: %e", arg, errno);
 	err = 1;
     } else {
 	int arglen = strlen(arg) + 1;
@@ -462,7 +466,7 @@ dormr(char *nam, char *arg, char *rp, char *ops, struct dirsav *ds, int first)
 	    narg[arglen-1] = '/';
 	    strcpy(narg + arglen, fn);
 	    unmetafy(fn, NULL);
-	    err |= dorm(nam, narg, fn, ops, &dsav, 0);
+	    err |= recursivecmd_doone(reccmd, narg, fn, &dsav, 0);
 	    fn += l;
 	}
 	hrealloc(files, fileslen, 0);
@@ -471,25 +475,219 @@ dormr(char *nam, char *arg, char *rp, char *ops, struct dirsav *ds, int first)
     if (err & 2)
 	return 2;
     if (restoredir(ds)) {
-	if(!ops['f'])
-	    zwarnnam(nam, "failed to return to previous directory: %e",
+	if(!reccmd->opt_noerr)
+	    zwarnnam(reccmd->nam, "failed to return to previous directory: %e",
 		     NULL, errno);
 	return 2;
     }
-    if(!ops['f'] && ops['i']) {
-	nicezputs(nam, stderr);
+    return err | reccmd->dirpost_func(arg, rp, sp, reccmd->magic);
+}
+
+/**/
+static int
+recurse_donothing(char *arg, char *rp, struct stat const *sp, void *magic)
+{
+    return 0;
+}
+
+/* rm builtin */
+
+struct rmmagic {
+    char *nam;
+    int opt_force;
+    int opt_interact;
+    int opt_unlinkdir;
+};
+
+/**/
+static int
+rm_leaf(char *arg, char *rp, struct stat const *sp, void *magic)
+{
+    struct rmmagic *rmm = magic;
+    struct stat st;
+
+    if(!rmm->opt_unlinkdir || !rmm->opt_force) {
+	if(!sp) {
+	    if(!lstat(rp, &st))
+		sp = &st;
+	}
+	if(sp) {
+	    if(!rmm->opt_unlinkdir && S_ISDIR(sp->st_mode)) {
+		if(rmm->opt_force)
+		    return 0;
+		zwarnnam(rmm->nam, "%s: %e", arg, EISDIR);
+		return 1;
+	    }
+	    if(rmm->opt_interact) {
+		nicezputs(rmm->nam, stderr);
+		fputs(": remove `", stderr);
+		nicezputs(arg, stderr);
+		fputs("'? ", stderr);
+		fflush(stderr);
+		if(!ask())
+		    return 0;
+	    } else if(!rmm->opt_force &&
+		    !S_ISLNK(sp->st_mode) &&
+		    access(rp, W_OK)) {
+		nicezputs(rmm->nam, stderr);
+		fputs(": remove `", stderr);
+		nicezputs(arg, stderr);
+		fprintf(stderr, "', overriding mode %04o? ",
+		    mode_to_octal(sp->st_mode));
+		fflush(stderr);
+		if(!ask())
+		    return 0;
+	    }
+	}
+    }
+    if(unlink(rp) && !rmm->opt_force) {
+	zwarnnam(rmm->nam, "%s: %e", arg, errno);
+	return 1;
+    }
+    return 0;
+}
+
+/**/
+static int
+rm_dirpost(char *arg, char *rp, struct stat const *sp, void *magic)
+{
+    struct rmmagic *rmm = magic;
+
+    if(rmm->opt_interact) {
+	nicezputs(rmm->nam, stderr);
 	fputs(": remove `", stderr);
 	nicezputs(arg, stderr);
 	fputs("'? ", stderr);
 	fflush(stderr);
 	if(!ask())
-	    return err;
+	    return 0;
     }
-    if(!rmdir(rp))
-	return err;
-    if(!ops['f'])
-	zwarnnam(nam, "%s: %e", arg, errno);
-    return 1;
+    if(rmdir(rp) && !rmm->opt_force) {
+	zwarnnam(rmm->nam, "%s: %e", arg, errno);
+	return 1;
+    }
+    return 0;
+}
+
+/**/
+static int
+bin_rm(char *nam, char **args, char *ops, int func)
+{
+    struct rmmagic rmm;
+    int err;
+
+    rmm.nam = nam;
+    rmm.opt_force = ops['f'];
+    rmm.opt_interact = ops['i'] && !ops['f'];
+    rmm.opt_unlinkdir = ops['d'];
+    err = recursivecmd(nam, ops['f'], ops['r'] && !ops['d'], ops['s'],
+	args, recurse_donothing, rm_dirpost, rm_leaf, &rmm);
+    return ops['f'] ? 0 : err;
+}
+
+/* chown builtin */
+
+struct chownmagic {
+    char *nam;
+    uid_t uid;
+    gid_t gid;
+};
+
+/**/
+static int
+chown_dochown(char *arg, char *rp, struct stat const *sp, void *magic)
+{
+    struct chownmagic *chm = magic;
+
+    if(lchown(rp, chm->uid, chm->gid)) {
+	zwarnnam(chm->nam, "%s: %e", arg, errno);
+	return 1;
+    }
+    return 0;
+}
+
+/**/
+static unsigned long getnumeric(char *p, int *errp)
+{
+    unsigned long ret;
+
+    if(*p < '0' || *p > '9') {
+	*errp = 1;
+	return 0;
+    }
+    ret = strtoul(p, &p, 10);
+    *errp = !!*p;
+    return ret;
+}
+
+enum { BIN_CHOWN, BIN_CHGRP };
+
+/**/
+static int
+bin_chown(char *nam, char **args, char *ops, int func)
+{
+    struct chownmagic chm;
+    char *uspec = ztrdup(*args), *p = uspec;
+
+    chm.nam = nam;
+    if(func == BIN_CHGRP) {
+	chm.uid = -1;
+	goto dogroup;
+    }
+    if(*p == ':' || *p == '.') {
+	chm.uid = -1;
+	p++;
+	goto dogroup;
+    } else {
+	struct passwd *pwd;
+	char *end = strchr(p, ':');
+	if(!end)
+	    end = strchr(p, '.');
+	if(end)
+	    *end = 0;
+	pwd = getpwnam(p);
+	if(pwd)
+	    chm.uid = pwd->pw_uid;
+	else {
+	    int err;
+	    chm.uid = getnumeric(p, &err);
+	    if(err) {
+		zwarnnam(nam, "%s: no such user", p, 0);
+		free(uspec);
+		return 1;
+	    }
+	}
+	if(end) {
+	    p = end+1;
+	    if(!*p) {
+		if(!pwd && !(pwd = getpwuid(chm.uid))) {
+		    zwarnnam(nam, "%s: no such user", uspec, 0);
+		    free(uspec);
+		    return 1;
+		}
+		chm.gid = pwd->pw_gid;
+	    } else {
+		struct group *grp;
+		dogroup:
+		grp = getgrnam(p);
+		if(grp)
+		    chm.gid = grp->gr_gid;
+		else {
+		    int err;
+		    chm.gid = getnumeric(p, &err);
+		    if(err) {
+			zwarnnam(nam, "%s: no such group", p, 0);
+			free(uspec);
+			return 1;
+		    }
+		}
+	    }
+	 } else
+	    chm.gid = -1;
+    }
+    free(uspec);
+    return recursivecmd(nam, 0, ops['R'], ops['s'],
+	args + 1, chown_dochown, recurse_donothing, chown_dochown, &chm);
 }
 
 /* module paraphernalia */
@@ -501,12 +699,14 @@ dormr(char *nam, char *arg, char *rp, char *ops, struct dirsav *ds, int first)
 #endif
 
 static struct builtin bintab[] = {
-    BUILTIN("ln",    0, bin_ln,    1, -1, BIN_LN, LN_OPTS, NULL),
-    BUILTIN("mkdir", 0, bin_mkdir, 1, -1, 0,      "pm",    NULL),
-    BUILTIN("mv",    0, bin_ln,    2, -1, BIN_MV, "fi",    NULL),
-    BUILTIN("rm",    0, bin_rm,    1, -1, 0,      "dfirs", NULL),
-    BUILTIN("rmdir", 0, bin_rmdir, 1, -1, 0,      NULL,    NULL),
-    BUILTIN("sync",  0, bin_sync,  0,  0, 0,      NULL,    NULL),
+    BUILTIN("chgrp", 0, bin_chown, 2, -1, BIN_CHGRP, "Rs",    NULL),
+    BUILTIN("chown", 0, bin_chown, 2, -1, BIN_CHOWN, "Rs",    NULL),
+    BUILTIN("ln",    0, bin_ln,    1, -1, BIN_LN,    LN_OPTS, NULL),
+    BUILTIN("mkdir", 0, bin_mkdir, 1, -1, 0,         "pm",    NULL),
+    BUILTIN("mv",    0, bin_ln,    2, -1, BIN_MV,    "fi",    NULL),
+    BUILTIN("rm",    0, bin_rm,    1, -1, 0,         "dfirs", NULL),
+    BUILTIN("rmdir", 0, bin_rmdir, 1, -1, 0,         NULL,    NULL),
+    BUILTIN("sync",  0, bin_sync,  0,  0, 0,         NULL,    NULL),
 };
 
 /**/
diff --git a/Src/Modules/files.mdd b/Src/Modules/files.mdd
index 236ca2d5a..171790b43 100644
--- a/Src/Modules/files.mdd
+++ b/Src/Modules/files.mdd
@@ -1,3 +1,3 @@
-autobins="ln mkdir mv rm rmdir sync"
+autobins="chgrp chown ln mkdir mv rm rmdir sync"
 
 objects="files.o"
diff --git a/Src/system.h b/Src/system.h
index 67f919bc8..31be575ef 100644
--- a/Src/system.h
+++ b/Src/system.h
@@ -583,6 +583,10 @@ struct timezone {
 # define R_OK 4
 #endif
 
+#ifndef HAVE_LCHOWN
+# define lchown chown
+#endif
+
 #ifndef HAVE_MEMCPY
 # define memcpy memmove
 #endif
diff --git a/configure.in b/configure.in
index 7b0a51cd6..88af50bd8 100644
--- a/configure.in
+++ b/configure.in
@@ -784,7 +784,7 @@ zsh_STRUCT_MEMBER([
 dnl need to integrate this function
 dnl AC_FUNC_STRFTIME
 
-AC_CHECK_FUNCS(memcpy memmove \
+AC_CHECK_FUNCS(lchown memcpy memmove \
               strftime waitpid select poll tcsetpgrp tcgetattr strstr lstat \
               getlogin setpgid gettimeofday gethostname mkfifo wait3 difftime \
               sigblock sigsetmask sigrelse sighold killpg sigaction getrlimit \