X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=coreutils%2Fchmod.c;h=40f681fb6ab0797da76d044e3148ef150924ee1c;hb=6b1e3d7e734f85a08c2e4414764f03a7f880b3e6;hp=53230b568833a608838f3e060db9d60200d35bd0;hpb=bdfd0d78bc44e73d693510e70087857785b3b521;p=oweals%2Fbusybox.git diff --git a/coreutils/chmod.c b/coreutils/chmod.c index 53230b568..40f681fb6 100644 --- a/coreutils/chmod.c +++ b/coreutils/chmod.c @@ -2,83 +2,159 @@ /* * Mini chmod implementation for busybox * - * Copyright (C) 1999,2000 by Lineo, inc. and Erik Andersen - * Copyright (C) 1999,2000,2001 by Erik Andersen + * Copyright (C) 1999-2004 by Erik Andersen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Reworked by (C) 2002 Vladimir Oleynik + * to correctly parse '-rwxgoa' * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. */ -#include -#include -#include -#include -#include -#include "busybox.h" +/* BB_AUDIT SUSv3 compliant */ +/* BB_AUDIT GNU defects - unsupported long options. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */ + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ -static int fileAction(const char *fileName, struct stat *statbuf, void* junk) -{ - if (!parse_mode((char *)junk, &(statbuf->st_mode))) - error_msg_and_die("internal error"); - if (chmod(fileName, statbuf->st_mode) == 0) - return (TRUE); - perror(fileName); - return (FALSE); -} -int chmod_main(int argc, char **argv) +#define OPT_RECURSE (option_mask32 & 1) +#define OPT_VERBOSE (USE_DESKTOP(option_mask32 & 2) SKIP_DESKTOP(0)) +#define OPT_CHANGED (USE_DESKTOP(option_mask32 & 4) SKIP_DESKTOP(0)) +#define OPT_QUIET (USE_DESKTOP(option_mask32 & 8) SKIP_DESKTOP(0)) +#define OPT_STR "R" USE_DESKTOP("vcf") + +/* coreutils: + * chmod never changes the permissions of symbolic links; the chmod + * system call cannot change their permissions. This is not a problem + * since the permissions of symbolic links are never used. + * However, for each symbolic link listed on the command line, chmod changes + * the permissions of the pointed-to file. In contrast, chmod ignores + * symbolic links encountered during recursive directory traversals. + */ + +static int FAST_FUNC fileAction(const char *fileName, struct stat *statbuf, void* param, int depth) { - int i; - int opt; - int recursiveFlag = FALSE; - - /* do normal option parsing */ - while ((opt = getopt(argc, argv, "R")) > 0) { - switch (opt) { - case 'R': - recursiveFlag = TRUE; - break; - default: - show_usage(); + mode_t newmode; + + /* match coreutils behavior */ + if (depth == 0) { + /* statbuf holds lstat result, but we need stat (follow link) */ + if (stat(fileName, statbuf)) + goto err; + } else { /* depth > 0: skip links */ + if (S_ISLNK(statbuf->st_mode)) + return TRUE; + } + newmode = statbuf->st_mode; + + if (!bb_parse_mode((char *)param, &newmode)) + bb_error_msg_and_die("invalid mode: %s", (char *)param); + + if (chmod(fileName, newmode) == 0) { + if (OPT_VERBOSE + || (OPT_CHANGED && statbuf->st_mode != newmode) + ) { + printf("mode of '%s' changed to %04o (%s)\n", fileName, + newmode & 07777, bb_mode_string(newmode)+1); } + return TRUE; } + err: + if (!OPT_QUIET) + bb_simple_perror_msg(fileName); + return FALSE; +} + +int chmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int chmod_main(int argc UNUSED_PARAM, char **argv) +{ + int retval = EXIT_SUCCESS; + char *arg, **argp; + char *smode; - if (argc > optind && argc > 2 && argv[optind]) { - /* Parse the specified mode */ - mode_t mode; - if (parse_mode(argv[optind], &mode) == FALSE) { - error_msg_and_die( "unknown mode: %s", argv[optind]); + /* Convert first encountered -r into ar, -w into aw etc + * so that getopt would not eat it */ + argp = argv; + while ((arg = *++argp)) { + /* Mode spec must be the first arg (sans -R etc) */ + /* (protect against mishandling e.g. "chmod 644 -r") */ + if (arg[0] != '-') { + arg = NULL; + break; + } + /* An option. Not a -- or valid option? */ + if (arg[1] && !strchr("-"OPT_STR, arg[1])) { + arg[0] = 'a'; + break; } - } else { - error_msg_and_die(too_few_args); } + /* Parse options */ + opt_complementary = "-2"; + getopt32(argv, ("-"OPT_STR) + 1); /* Reuse string */ + argv += optind; + + /* Restore option-like mode if needed */ + if (arg) arg[0] = '-'; + /* Ok, ready to do the deed now */ - for (i = optind + 1; i < argc; i++) { - if (recursive_action (argv[i], recursiveFlag, FALSE, FALSE, fileAction, - fileAction, argv[optind]) == FALSE) { - return EXIT_FAILURE; + smode = *argv++; + do { + if (!recursive_action(*argv, + OPT_RECURSE, // recurse + fileAction, // file action + fileAction, // dir action + smode, // user data + 0) // depth + ) { + retval = EXIT_FAILURE; } - } - return EXIT_SUCCESS; + } while (*++argv); + + return retval; } /* -Local Variables: -c-file-style: "linux" -c-basic-offset: 4 -tab-width: 4 -End: +Security: chmod is too important and too subtle. +This is a test script (busybox chmod versus coreutils). +Run it in empty directory. + +#!/bin/sh +t1="/tmp/busybox chmod" +t2="/usr/bin/chmod" +create() { + rm -rf $1; mkdir $1 + ( + cd $1 || exit 1 + mkdir dir + >up + >file + >dir/file + ln -s dir linkdir + ln -s file linkfile + ln -s ../up dir/up + ) +} +tst() { + (cd test1; $t1 $1) + (cd test2; $t2 $1) + (cd test1; ls -lR) >out1 + (cd test2; ls -lR) >out2 + echo "chmod $1" >out.diff + if ! diff -u out1 out2 >>out.diff; then exit 1; fi + rm out.diff +} +echo "If script produced 'out.diff' file, then at least one testcase failed" +create test1; create test2 +tst "a+w file" +tst "a-w dir" +tst "a+w linkfile" +tst "a-w linkdir" +tst "-R a+w file" +tst "-R a-w dir" +tst "-R a+w linkfile" +tst "-R a-w linkdir" +tst "a-r,a+x linkfile" */