X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=coreutils%2Fchmod.c;h=c04201eecfae0f7d1b503591bac94bc250a679e2;hb=e2b41cfb4be2486d9f2d50a8d750eed15c29320e;hp=390cc6d2c7b36b8d8e338677d510afaeeab95710;hpb=cb81e6484d1f50ec2761f6294722407b14add525;p=oweals%2Fbusybox.git diff --git a/coreutils/chmod.c b/coreutils/chmod.c index 390cc6d2c..c04201eec 100644 --- a/coreutils/chmod.c +++ b/coreutils/chmod.c @@ -2,100 +2,113 @@ /* * Mini chmod implementation for busybox * - * Copyright (C) 1999-2003 by Erik Andersen + * Copyright (C) 1999-2004 by Erik Andersen * * Reworked by (C) 2002 Vladimir Oleynik * to correctly parse '-rwxgoa' * - * 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 - * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. */ /* BB_AUDIT SUSv3 compliant */ -/* BB_AUDIT GNU defects - unsupported options -c, -f, -v, and long options. */ +/* BB_AUDIT GNU defects - unsupported long options. */ /* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */ -#include -#include -#include -#include -#include -#include "busybox.h" +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + + +#define OPT_RECURSE (option_mask32 & 1) +#define OPT_VERBOSE (IF_DESKTOP(option_mask32 & 2) IF_NOT_DESKTOP(0)) +#define OPT_CHANGED (IF_DESKTOP(option_mask32 & 4) IF_NOT_DESKTOP(0)) +#define OPT_QUIET (IF_DESKTOP(option_mask32 & 8) IF_NOT_DESKTOP(0)) +#define OPT_STR "R" IF_DESKTOP("vcf") -static int fileAction(const char *fileName, struct stat *statbuf, void* junk) +/* 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) { - if (!bb_parse_mode((char *)junk, &(statbuf->st_mode))) - bb_error_msg_and_die( "invalid mode: %s", (char *)junk); - if (chmod(fileName, statbuf->st_mode) == 0) - return (TRUE); - bb_perror_msg("%s", fileName); /* Avoid multibyte problems. */ - return (FALSE); + 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) +int chmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int chmod_main(int argc UNUSED_PARAM, char **argv) { int retval = EXIT_SUCCESS; - int recursiveFlag = FALSE; - int count; + char *arg, **argp; char *smode; - char **p; - char *p0; - char opt = '-'; - - ++argv; - count = 0; - - for (p = argv ; *p ; p++) { - p0 = p[0]; - if (p0[0] == opt) { - if ((p0[1] == '-') && !p0[2]) { - opt = 0; /* Disable further option processing. */ - continue; - } - if (p0[1] == 'R') { - char *s = p0 + 2; - while (*s == 'R') { - ++s; - } - if (*s) { - bb_show_usage(); - } - recursiveFlag = TRUE; - continue; - } - if (count) { - bb_show_usage(); - } + + /* 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; } - argv[count] = p0; - ++count; } - argv[count] = NULL; + /* Parse options */ + opt_complementary = "-2"; + getopt32(argv, ("-"OPT_STR) + 1); /* Reuse string */ + argv += optind; - if (count < 2) { - bb_show_usage(); - } - - smode = *argv; - ++argv; + /* Restore option-like mode if needed */ + if (arg) arg[0] = '-'; /* Ok, ready to do the deed now */ + smode = *argv++; do { - if (! recursive_action (*argv, recursiveFlag, FALSE, FALSE, - fileAction, fileAction, smode)) { + if (!recursive_action(*argv, + OPT_RECURSE, // recurse + fileAction, // file action + fileAction, // dir action + smode, // user data + 0) // depth + ) { retval = EXIT_FAILURE; } } while (*++argv); @@ -104,9 +117,44 @@ int chmod_main(int argc, char **argv) } /* -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" */