/* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ #if !defined(lint) && defined(DOSCCS) static char sccsid[] = "@(#)chmod.c 5.5.1 (2.11BSD GTE) 11/4/94"; #endif /* * chmod options mode files * where * mode is [ugoa][+-=][rwxXstugo] or an octal number * options are -Rf */ #include #include #include #include #include static char *fchdirmsg = "Can't fchdir() back to starting directory"; char *modestring, *ms; int um; int status; int fflag; int rflag; main(argc, argv) char *argv[]; { register char *p, *flags; register int i; struct stat st; int fcurdir; if (argc < 3) { fprintf(stderr, "Usage: chmod [-Rf] [ugoa][+-=][rwxXstugo] file ...\n"); exit(-1); } argv++, --argc; while (argc > 0 && argv[0][0] == '-') { for (p = &argv[0][1]; *p; p++) switch (*p) { case 'R': rflag++; break; case 'f': fflag++; break; default: goto done; } argc--, argv++; } done: modestring = argv[0]; um = umask(0); (void) newmode(0); if (rflag) { fcurdir = open(".", O_RDONLY); if (fcurdir < 0) fatal(255, "Can't open ."); } for (i = 1; i < argc; i++) { p = argv[i]; /* do stat for directory arguments */ if (lstat(p, &st) < 0) { status += Perror(p); continue; } if (rflag && (st.st_mode&S_IFMT) == S_IFDIR) { status += chmodr(p, newmode(st.st_mode), fcurdir); continue; } if ((st.st_mode&S_IFMT) == S_IFLNK && stat(p, &st) < 0) { status += Perror(p); continue; } if (chmod(p, newmode(st.st_mode)) < 0) { status += Perror(p); continue; } } close(fcurdir); exit(status); } chmodr(dir, mode, savedir) char *dir; int mode; int savedir; { register DIR *dirp; register struct direct *dp; struct stat st; int ecode; /* * Change what we are given before doing it's contents */ if (chmod(dir, newmode(mode)) < 0 && Perror(dir)) return (1); if (chdir(dir) < 0) { Perror(dir); return (1); } if ((dirp = opendir(".")) == NULL) { Perror(dir); return (1); } dp = readdir(dirp); dp = readdir(dirp); /* read "." and ".." */ ecode = 0; for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { if (lstat(dp->d_name, &st) < 0) { ecode = Perror(dp->d_name); if (ecode) break; continue; } if ((st.st_mode&S_IFMT) == S_IFDIR) { ecode = chmodr(dp->d_name, newmode(st.st_mode), dirfd(dirp)); if (ecode) break; continue; } if ((st.st_mode&S_IFMT) == S_IFLNK) continue; if (chmod(dp->d_name, newmode(st.st_mode)) < 0 && (ecode = Perror(dp->d_name))) break; } if (fchdir(savedir) < 0) fatal(255, fchdirmsg); closedir(dirp); return (ecode); } error(fmt, a) char *fmt, *a; { if (!fflag) { fprintf(stderr, "chmod: "); fprintf(stderr, fmt, a); putc('\n', stderr); } return (!fflag); } fatal(status, fmt, a) int status; char *fmt, *a; { fflag = 0; (void) error(fmt, a); exit(status); } Perror(s) char *s; { if (!fflag) { fprintf(stderr, "chmod: "); perror(s); } return (!fflag); } newmode(nm) unsigned nm; { register o, m, b; int savem; ms = modestring; savem = nm; m = abs(); if (*ms == '\0') return (m); do { m = who(); while (o = what()) { b = where(nm); switch (o) { case '+': nm |= b & m; break; case '-': nm &= ~(b & m); break; case '=': nm &= ~m; nm |= b & m; break; } } } while (*ms++ == ','); if (*--ms) fatal(255, "invalid mode"); return (nm); } abs() { register c, i; i = 0; while ((c = *ms++) >= '0' && c <= '7') i = (i << 3) + (c - '0'); ms--; return (i); } #define USER 05700 /* user's bits */ #define GROUP 02070 /* group's bits */ #define OTHER 00007 /* other's bits */ #define ALL 01777 /* all (note absence of setuid, etc) */ #define READ 00444 /* read permit */ #define WRITE 00222 /* write permit */ #define EXEC 00111 /* exec permit */ #define SETID 06000 /* set[ug]id */ #define STICKY 01000 /* sticky bit */ who() { register m; m = 0; for (;;) switch (*ms++) { case 'u': m |= USER; continue; case 'g': m |= GROUP; continue; case 'o': m |= OTHER; continue; case 'a': m |= ALL; continue; default: ms--; if (m == 0) m = ALL & ~um; return (m); } } what() { switch (*ms) { case '+': case '-': case '=': return (*ms++); } return (0); } where(om) register om; { register m; m = 0; switch (*ms) { case 'u': m = (om & USER) >> 6; goto dup; case 'g': m = (om & GROUP) >> 3; goto dup; case 'o': m = (om & OTHER); dup: m &= (READ|WRITE|EXEC); m |= (m << 3) | (m << 6); ++ms; return (m); } for (;;) switch (*ms++) { case 'r': m |= READ; continue; case 'w': m |= WRITE; continue; case 'x': m |= EXEC; continue; case 'X': if ((om & S_IFDIR) || (om & EXEC)) m |= EXEC; continue; case 's': m |= SETID; continue; case 't': m |= STICKY; continue; default: ms--; return (m); } }