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/* BB_AUDIT GNU defects - unsupported long options. */ 12/* http://www.opengroup.org/onlinepubs/007904975/utilities/chown.html */ 13 14#include "libbb.h" 15 16/* This is a NOEXEC applet. Be very careful! */ 17 18 19#define OPT_STR ("Rh" USE_DESKTOP("vcfLHP")) 20#define BIT_RECURSE 1 21#define OPT_RECURSE (option_mask32 & 1) 22#define OPT_NODEREF (option_mask32 & 2) 23#define OPT_VERBOSE (USE_DESKTOP(option_mask32 & 0x04) SKIP_DESKTOP(0)) 24#define OPT_CHANGED (USE_DESKTOP(option_mask32 & 0x08) SKIP_DESKTOP(0)) 25#define OPT_QUIET (USE_DESKTOP(option_mask32 & 0x10) SKIP_DESKTOP(0)) 26/* POSIX options 27 * -L traverse every symbolic link to a directory encountered 28 * -H if a command line argument is a symbolic link to a directory, traverse it 29 * -P do not traverse any symbolic links (default) 30 * We do not conform to the following: 31 * "Specifying more than one of -H, -L, and -P is not an error. 32 * The last option specified shall determine the behavior of the utility." */ 33/* -L */ 34#define BIT_TRAVERSE 0x20 35#define OPT_TRAVERSE (USE_DESKTOP(option_mask32 & BIT_TRAVERSE) SKIP_DESKTOP(0)) 36/* -H or -L */ 37#define BIT_TRAVERSE_TOP (0x20|0x40) 38#define OPT_TRAVERSE_TOP (USE_DESKTOP(option_mask32 & BIT_TRAVERSE_TOP) SKIP_DESKTOP(0)) 39 40typedef int (*chown_fptr)(const char *, uid_t, gid_t); 41 42static struct bb_uidgid_t ugid = { -1, -1 }; 43 44static int fileAction(const char *fileName, struct stat *statbuf, 45 void *cf, int depth) 46{ 47 uid_t u = (ugid.uid == (uid_t)-1) ? statbuf->st_uid : ugid.uid; 48 gid_t g = (ugid.gid == (gid_t)-1) ? statbuf->st_gid : ugid.gid; 49 50 if (!((chown_fptr)cf)(fileName, u, g)) { 51 if (OPT_VERBOSE 52 || (OPT_CHANGED && (statbuf->st_uid != u || statbuf->st_gid != g)) 53 ) { 54 printf("changed ownership of '%s' to %u:%u\n", 55 fileName, (unsigned)u, (unsigned)g); 56 } 57 return TRUE; 58 } 59 if (!OPT_QUIET) 60 bb_perror_msg("%s", fileName); /* A filename can have % in it... */ 61 return FALSE; 62} 63 64int chown_main(int argc, char **argv); 65int chown_main(int argc, char **argv) 66{ 67 int retval = EXIT_SUCCESS; 68 int flags; 69 chown_fptr chown_func; 70 71 opt_complementary = "-2"; 72 getopt32(argv, OPT_STR); 73 argv += optind; 74 75 /* This matches coreutils behavior (almost - see below) */ 76 chown_func = chown; 77 if (OPT_NODEREF 78 /* || (OPT_RECURSE && !OPT_TRAVERSE_TOP): */ 79 USE_DESKTOP( || (option_mask32 & (BIT_RECURSE|BIT_TRAVERSE_TOP)) == BIT_RECURSE) 80 ) { 81 chown_func = lchown; 82 } 83 84 flags = ACTION_DEPTHFIRST; /* match coreutils order */ 85 if (OPT_RECURSE) 86 flags |= ACTION_RECURSE; 87 if (OPT_TRAVERSE_TOP) 88 flags |= ACTION_FOLLOWLINKS_L0; /* -H/-L: follow links on depth 0 */ 89 if (OPT_TRAVERSE) 90 flags |= ACTION_FOLLOWLINKS; /* follow links if -L */ 91 92 parse_chown_usergroup_or_die(&ugid, argv[0]); 93 94 /* Ok, ready to do the deed now */ 95 argv++; 96 do { 97 if (!recursive_action(*argv, 98 flags, /* flags */ 99 fileAction, /* file action */ 100 fileAction, /* dir action */ 101 chown_func, /* user data */ 102 0) /* depth */ 103 ) { 104 retval = EXIT_FAILURE; 105 } 106 } while (*++argv); 107 108 return retval; 109} 110 111/* 112Testcase. Run in empty directory. 113 114#!/bin/sh 115t1="/tmp/busybox chown" 116t2="/usr/bin/chown" 117create() { 118 rm -rf $1; mkdir $1 119 ( 120 cd $1 || exit 1 121 mkdir dir dir2 122 >up 123 >file 124 >dir/file 125 >dir2/file 126 ln -s dir linkdir 127 ln -s file linkfile 128 ln -s ../up dir/linkup 129 ln -s ../dir2 dir/linkupdir2 130 ) 131 chown -R 0:0 $1 132} 133tst() { 134 create test1 135 create test2 136 echo "[$1]" >>test1.out 137 echo "[$1]" >>test2.out 138 (cd test1; $t1 $1) >>test1.out 2>&1 139 (cd test2; $t2 $1) >>test2.out 2>&1 140 (cd test1; ls -lnR) >out1 141 (cd test2; ls -lnR) >out2 142 echo "chown $1" >out.diff 143 if ! diff -u out1 out2 >>out.diff; then exit 1; fi 144 rm out.diff 145} 146tst_for_each() { 147 tst "$1 1:1 file" 148 tst "$1 1:1 dir" 149 tst "$1 1:1 linkdir" 150 tst "$1 1:1 linkfile" 151} 152echo "If script produced 'out.diff' file, then at least one testcase failed" 153>test1.out 154>test2.out 155# These match coreutils 6.8: 156tst_for_each "-v" 157tst_for_each "-vR" 158tst_for_each "-vRP" 159tst_for_each "-vRL" 160tst_for_each "-vRH" 161tst_for_each "-vh" 162tst_for_each "-vhR" 163tst_for_each "-vhRP" 164tst_for_each "-vhRL" 165tst_for_each "-vhRH" 166# Fix `name' in coreutils output 167sed 's/`/'"'"'/g' -i test2.out 168# Compare us with coreutils output 169diff -u test1.out test2.out 170 171*/ 172