1/* vi: set sw=4 ts=4: */ 2/* 3 * Mini chmod implementation for busybox 4 * 5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> 6 * 7 * Reworked by (C) 2002 Vladimir Oleynik <dzo@simtreas.ru> 8 * to correctly parse '-rwxgoa' 9 * 10 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. 11 */ 12 13/* BB_AUDIT SUSv3 compliant */ 14/* BB_AUDIT GNU defects - unsupported long options. */ 15/* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */ 16 17#include "libbb.h" 18 19/* This is a NOEXEC applet. Be very careful! */ 20 21 22#define OPT_RECURSE (option_mask32 & 1) 23#define OPT_VERBOSE (IF_DESKTOP(option_mask32 & 2) IF_NOT_DESKTOP(0)) 24#define OPT_CHANGED (IF_DESKTOP(option_mask32 & 4) IF_NOT_DESKTOP(0)) 25#define OPT_QUIET (IF_DESKTOP(option_mask32 & 8) IF_NOT_DESKTOP(0)) 26#define OPT_STR "R" IF_DESKTOP("vcf") 27 28/* coreutils: 29 * chmod never changes the permissions of symbolic links; the chmod 30 * system call cannot change their permissions. This is not a problem 31 * since the permissions of symbolic links are never used. 32 * However, for each symbolic link listed on the command line, chmod changes 33 * the permissions of the pointed-to file. In contrast, chmod ignores 34 * symbolic links encountered during recursive directory traversals. 35 */ 36 37static int FAST_FUNC fileAction(const char *fileName, struct stat *statbuf, void* param, int depth) 38{ 39 mode_t newmode; 40 41 /* match coreutils behavior */ 42 if (depth == 0) { 43 /* statbuf holds lstat result, but we need stat (follow link) */ 44 if (stat(fileName, statbuf)) 45 goto err; 46 } else { /* depth > 0: skip links */ 47 if (S_ISLNK(statbuf->st_mode)) 48 return TRUE; 49 } 50 newmode = statbuf->st_mode; 51 52 if (!bb_parse_mode((char *)param, &newmode)) 53 bb_error_msg_and_die("invalid mode: %s", (char *)param); 54 55 if (chmod(fileName, newmode) == 0) { 56 if (OPT_VERBOSE 57 || (OPT_CHANGED && statbuf->st_mode != newmode) 58 ) { 59 printf("mode of '%s' changed to %04o (%s)\n", fileName, 60 newmode & 07777, bb_mode_string(newmode)+1); 61 } 62 return TRUE; 63 } 64 err: 65 if (!OPT_QUIET) 66 bb_simple_perror_msg(fileName); 67 return FALSE; 68} 69 70int chmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 71int chmod_main(int argc UNUSED_PARAM, char **argv) 72{ 73 int retval = EXIT_SUCCESS; 74 char *arg, **argp; 75 char *smode; 76 77 /* Convert first encountered -r into ar, -w into aw etc 78 * so that getopt would not eat it */ 79 argp = argv; 80 while ((arg = *++argp)) { 81 /* Mode spec must be the first arg (sans -R etc) */ 82 /* (protect against mishandling e.g. "chmod 644 -r") */ 83 if (arg[0] != '-') { 84 arg = NULL; 85 break; 86 } 87 /* An option. Not a -- or valid option? */ 88 if (arg[1] && !strchr("-"OPT_STR, arg[1])) { 89 arg[0] = 'a'; 90 break; 91 } 92 } 93 94 /* Parse options */ 95 opt_complementary = "-2"; 96 getopt32(argv, ("-"OPT_STR) + 1); /* Reuse string */ 97 argv += optind; 98 99 /* Restore option-like mode if needed */ 100 if (arg) arg[0] = '-'; 101 102 /* Ok, ready to do the deed now */ 103 smode = *argv++; 104 do { 105 if (!recursive_action(*argv, 106 OPT_RECURSE, // recurse 107 fileAction, // file action 108 fileAction, // dir action 109 smode, // user data 110 0) // depth 111 ) { 112 retval = EXIT_FAILURE; 113 } 114 } while (*++argv); 115 116 return retval; 117} 118 119/* 120Security: chmod is too important and too subtle. 121This is a test script (busybox chmod versus coreutils). 122Run it in empty directory. 123 124#!/bin/sh 125t1="/tmp/busybox chmod" 126t2="/usr/bin/chmod" 127create() { 128 rm -rf $1; mkdir $1 129 ( 130 cd $1 || exit 1 131 mkdir dir 132 >up 133 >file 134 >dir/file 135 ln -s dir linkdir 136 ln -s file linkfile 137 ln -s ../up dir/up 138 ) 139} 140tst() { 141 (cd test1; $t1 $1) 142 (cd test2; $t2 $1) 143 (cd test1; ls -lR) >out1 144 (cd test2; ls -lR) >out2 145 echo "chmod $1" >out.diff 146 if ! diff -u out1 out2 >>out.diff; then exit 1; fi 147 rm out.diff 148} 149echo "If script produced 'out.diff' file, then at least one testcase failed" 150create test1; create test2 151tst "a+w file" 152tst "a-w dir" 153tst "a+w linkfile" 154tst "a-w linkdir" 155tst "-R a+w file" 156tst "-R a-w dir" 157tst "-R a+w linkfile" 158tst "-R a-w linkdir" 159tst "a-r,a+x linkfile" 160*/ 161