1/* vi: set sw=4 ts=4: */ 2/* 3 * Mini chown implementation for busybox 4 * 5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> 6 * 7 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. 8 */ 9 10/* BB_AUDIT SUSv3 defects - none? */ 11/* http://www.opengroup.org/onlinepubs/007904975/utilities/chown.html */ 12 13#include "libbb.h" 14 15/* This is a NOEXEC applet. Be very careful! */ 16 17 18#define OPT_STR ("Rh" IF_DESKTOP("vcfLHP")) 19#define BIT_RECURSE 1 20#define OPT_RECURSE (opt & 1) 21#define OPT_NODEREF (opt & 2) 22#define OPT_VERBOSE (IF_DESKTOP(opt & 0x04) IF_NOT_DESKTOP(0)) 23#define OPT_CHANGED (IF_DESKTOP(opt & 0x08) IF_NOT_DESKTOP(0)) 24#define OPT_QUIET (IF_DESKTOP(opt & 0x10) IF_NOT_DESKTOP(0)) 25/* POSIX options 26 * -L traverse every symbolic link to a directory encountered 27 * -H if a command line argument is a symbolic link to a directory, traverse it 28 * -P do not traverse any symbolic links (default) 29 * We do not conform to the following: 30 * "Specifying more than one of -H, -L, and -P is not an error. 31 * The last option specified shall determine the behavior of the utility." */ 32/* -L */ 33#define BIT_TRAVERSE 0x20 34#define OPT_TRAVERSE (IF_DESKTOP(opt & BIT_TRAVERSE) IF_NOT_DESKTOP(0)) 35/* -H or -L */ 36#define BIT_TRAVERSE_TOP (0x20|0x40) 37#define OPT_TRAVERSE_TOP (IF_DESKTOP(opt & BIT_TRAVERSE_TOP) IF_NOT_DESKTOP(0)) 38 39#if ENABLE_FEATURE_CHOWN_LONG_OPTIONS 40static const char chown_longopts[] ALIGN1 = 41 "recursive\0" No_argument "R" 42 "dereference\0" No_argument "\xff" 43 "no-dereference\0" No_argument "h" 44# if ENABLE_DESKTOP 45 "changes\0" No_argument "c" 46 "silent\0" No_argument "f" 47 "quiet\0" No_argument "f" 48 "verbose\0" No_argument "v" 49# endif 50 ; 51#endif 52 53typedef int (*chown_fptr)(const char *, uid_t, gid_t); 54 55struct param_t { 56 struct bb_uidgid_t ugid; 57 chown_fptr chown_func; 58}; 59 60static int FAST_FUNC fileAction(const char *fileName, struct stat *statbuf, 61 void *vparam, int depth UNUSED_PARAM) 62{ 63#define param (*(struct param_t*)vparam) 64#define opt option_mask32 65 uid_t u = (param.ugid.uid == (uid_t)-1L) ? statbuf->st_uid : param.ugid.uid; 66 gid_t g = (param.ugid.gid == (gid_t)-1L) ? statbuf->st_gid : param.ugid.gid; 67 68 if (param.chown_func(fileName, u, g) == 0) { 69 if (OPT_VERBOSE 70 || (OPT_CHANGED && (statbuf->st_uid != u || statbuf->st_gid != g)) 71 ) { 72 printf("changed ownership of '%s' to %u:%u\n", 73 fileName, (unsigned)u, (unsigned)g); 74 } 75 return TRUE; 76 } 77 if (!OPT_QUIET) 78 bb_simple_perror_msg(fileName); 79 return FALSE; 80#undef opt 81#undef param 82} 83 84int chown_main(int argc UNUSED_PARAM, char **argv) 85{ 86 int retval = EXIT_SUCCESS; 87 int opt, flags; 88 struct param_t param; 89 90 /* Just -1 might not work: uid_t may be unsigned long */ 91 param.ugid.uid = -1L; 92 param.ugid.gid = -1L; 93 94#if ENABLE_FEATURE_CHOWN_LONG_OPTIONS 95 applet_long_options = chown_longopts; 96#endif 97 opt_complementary = "-2"; 98 opt = getopt32(argv, OPT_STR); 99 argv += optind; 100 101 /* This matches coreutils behavior (almost - see below) */ 102 param.chown_func = chown; 103 if (OPT_NODEREF 104 /* || (OPT_RECURSE && !OPT_TRAVERSE_TOP): */ 105 IF_DESKTOP( || (opt & (BIT_RECURSE|BIT_TRAVERSE_TOP)) == BIT_RECURSE) 106 ) { 107 param.chown_func = lchown; 108 } 109 110 flags = ACTION_DEPTHFIRST; /* match coreutils order */ 111 if (OPT_RECURSE) 112 flags |= ACTION_RECURSE; 113 if (OPT_TRAVERSE_TOP) 114 flags |= ACTION_FOLLOWLINKS_L0; /* -H/-L: follow links on depth 0 */ 115 if (OPT_TRAVERSE) 116 flags |= ACTION_FOLLOWLINKS; /* follow links if -L */ 117 118 parse_chown_usergroup_or_die(¶m.ugid, argv[0]); 119 120 /* Ok, ready to do the deed now */ 121 while (*++argv) { 122 if (!recursive_action(*argv, 123 flags, /* flags */ 124 fileAction, /* file action */ 125 fileAction, /* dir action */ 126 ¶m, /* user data */ 127 0) /* depth */ 128 ) { 129 retval = EXIT_FAILURE; 130 } 131 } 132 133 return retval; 134} 135 136/* 137Testcase. Run in empty directory. 138 139#!/bin/sh 140t1="/tmp/busybox chown" 141t2="/usr/bin/chown" 142create() { 143 rm -rf $1; mkdir $1 144 ( 145 cd $1 || exit 1 146 mkdir dir dir2 147 >up 148 >file 149 >dir/file 150 >dir2/file 151 ln -s dir linkdir 152 ln -s file linkfile 153 ln -s ../up dir/linkup 154 ln -s ../dir2 dir/linkupdir2 155 ) 156 chown -R 0:0 $1 157} 158tst() { 159 create test1 160 create test2 161 echo "[$1]" >>test1.out 162 echo "[$1]" >>test2.out 163 (cd test1; $t1 $1) >>test1.out 2>&1 164 (cd test2; $t2 $1) >>test2.out 2>&1 165 (cd test1; ls -lnR) >out1 166 (cd test2; ls -lnR) >out2 167 echo "chown $1" >out.diff 168 if ! diff -u out1 out2 >>out.diff; then exit 1; fi 169 rm out.diff 170} 171tst_for_each() { 172 tst "$1 1:1 file" 173 tst "$1 1:1 dir" 174 tst "$1 1:1 linkdir" 175 tst "$1 1:1 linkfile" 176} 177echo "If script produced 'out.diff' file, then at least one testcase failed" 178>test1.out 179>test2.out 180# These match coreutils 6.8: 181tst_for_each "-v" 182tst_for_each "-vR" 183tst_for_each "-vRP" 184tst_for_each "-vRL" 185tst_for_each "-vRH" 186tst_for_each "-vh" 187tst_for_each "-vhR" 188tst_for_each "-vhRP" 189tst_for_each "-vhRL" 190tst_for_each "-vhRH" 191# Fix `name' in coreutils output 192sed 's/`/'"'"'/g' -i test2.out 193# Compare us with coreutils output 194diff -u test1.out test2.out 195 196*/ 197