76f3333e3b24aae40b7da34bf879f2d1f7ae88e9
[oweals/busybox.git] / coreutils / chown.c
1 /*
2  * Mini chown/chmod/chgrp implementation for busybox
3  *
4  * Copyright (C) 1998 by Erik Andersen <andersee@debian.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  */
21
22 #include <stdio.h>
23 #include <grp.h>
24 #include <pwd.h>
25 #include "internal.h"
26
27
28 static int uid=-1;
29 static int gid=0;
30 static int whichApp;
31 static char* invocationName=NULL;
32 static mode_t mode=7777;
33
34
35 #define CHGRP_APP   1
36 #define CHOWN_APP   2
37 #define CHMOD_APP   3
38
39 static const char chgrp_usage[] = "[OPTION]... GROUP FILE...\n"
40     "Change the group membership of each FILE to GROUP.\n"
41     "\n\tOptions:\n" "\t-R\tchange files and directories recursively\n";
42 static const char chown_usage[] = "[OPTION]...  OWNER[.[GROUP] FILE...\n"
43     "Change the owner and/or group of each FILE to OWNER and/or GROUP.\n"
44     "\n\tOptions:\n" "\t-R\tchange files and directories recursively\n";
45 static const char chmod_usage[] = "[-R] MODE[,MODE]... FILE...\n"
46 "Each MODE is one or more of the letters ugoa, one of the symbols +-= and\n"
47 "one or more of the letters rwxst.\n\n"
48  "\t-R\tchange files and directories recursively.\n";
49
50
51
52 static int fileAction(const char *fileName)
53 {
54     struct stat statBuf;
55     if (stat(fileName, &statBuf) < 0) {
56         switch (whichApp) {
57             case CHGRP_APP:
58             case CHOWN_APP:
59                 if (chown(fileName, ((whichApp==CHOWN_APP)? uid: statBuf.st_uid), gid) < 0)
60                     return( TRUE);
61             case CHMOD_APP:
62                 if (chmod(fileName, mode))
63                     return( TRUE);
64         }
65     }
66     perror(fileName);
67     return( FALSE);
68 }
69
70 /* [ugoa]{+|-|=}[rwxstl] */
71 int parse_mode( const char* s, mode_t* or, mode_t* and, int* group_execute) 
72 {
73         mode_t  mode = 0;
74         mode_t  groups = S_ISVTX;
75         char    type;
76         char    c;
77
78         do {
79                 for ( ; ; ) {
80                         switch ( c = *s++ ) {
81                         case '\0':
82                                 return (FALSE);
83                         case 'u':
84                                 groups |= S_ISUID|S_IRWXU;
85                                 continue;
86                         case 'g':
87                                 groups |= S_ISGID|S_IRWXG;
88                                 continue;
89                         case 'o':
90                                 groups |= S_IRWXO;
91                                 continue;
92                         case 'a':
93                                 groups |= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
94                                 continue;
95                         case '+':
96                         case '=':
97                         case '-':
98                                 type = c;
99                                 if ( groups == S_ISVTX ) /* The default is "all" */
100                                         groups |= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
101                                 break;
102                         default:
103                                 if ( c >= '0' && c <= '7' && mode == 0 && groups == S_ISVTX ) {
104                                         *and = 0;
105                                         *or = strtol(--s, 0, 010);
106                                         return (TRUE);
107                                 }
108                                 else
109                                         return (FALSE);
110                         }
111                         break;
112                 }
113
114                 while ( (c = *s++) != '\0' ) {
115                         switch ( c ) {
116                         case ',':
117                                 break;
118                         case 'r':
119                                 mode |= S_IRUSR|S_IRGRP|S_IROTH;
120                                 continue;
121                         case 'w':
122                                 mode |= S_IWUSR|S_IWGRP|S_IWOTH;
123                                 continue;
124                         case 'x':
125                                 mode |= S_IXUSR|S_IXGRP|S_IXOTH;
126                                 continue;
127                         case 's':
128                                 if ( group_execute != 0 && (groups & S_IRWXG) ) {
129                                         if ( *group_execute < 0 )
130                                                 return (FALSE);
131                                         if ( type != '-' ) {
132                                                 mode |= S_IXGRP;
133                                                 *group_execute = 1;
134                                         }
135                                 }
136                                 mode |= S_ISUID|S_ISGID;
137                                 continue;
138                         case 'l':
139                                 if ( *group_execute > 0 )
140                                         return (FALSE);
141                                 if ( type != '-' ) {
142                                         *and &= ~S_IXGRP;
143                                         *group_execute = -1;
144                                 }
145                                 mode |= S_ISGID;
146                                 groups |= S_ISGID;
147                                 continue;
148                         case 't':
149                                 mode |= S_ISVTX;
150                                 continue;
151                         default:
152                                 return (FALSE);
153                         }
154                         break;
155                 }
156                 switch ( type ) {
157                 case '=':
158                         *and &= ~(groups);
159                         /* fall through */
160                 case '+':
161                         *or |= mode & groups;
162                         break;
163                 case '-':
164                         *and &= ~(mode & groups);
165                         *or &= *and;
166                         break;
167                 }
168         } while ( c == ',' );
169         return (TRUE);
170 }
171
172
173 int chmod_chown_chgrp_main(int argc, char **argv)
174 {
175     struct group *grp;
176     struct passwd *pwd;
177     int recursiveFlag=FALSE;
178     char *groupName;
179
180
181     whichApp = (strcmp(*argv, "chown")==0)? CHOWN_APP : (strcmp(*argv, "chmod")==0)? CHMOD_APP : CHGRP_APP; 
182
183     if (argc < 2) {
184         fprintf(stderr, "Usage: %s %s", *argv, 
185                 (whichApp==TRUE)? chown_usage : chgrp_usage);
186         exit( FALSE);
187     }
188     invocationName=*argv;
189     argc--;
190     argv++;
191
192     /* Parse options */
193     while (**argv == '-') {
194         while (*++(*argv)) switch (**argv) {
195             case 'R':
196                 recursiveFlag = TRUE;
197                 break;
198             default:
199                 fprintf(stderr, "Unknown option: %c\n", **argv);
200                 exit( FALSE);
201         }
202         argc--;
203         argv++;
204     }
205     
206     if ( whichApp == CHMOD_APP ) {
207         /* Find the specified modes */
208         if ( parse_mode(*argv, &orWithMode, &andWithMode, 0) == FALSE ) {
209             fprintf(stderr, "%s: Unknown mode: %s\n", invocationName, *argv);
210             exit( FALSE);
211         }
212         mode &= andWithMode;
213         mode |= orWithMode;
214     } else {
215
216         /* Find the selected group */
217         groupName = strchr(*argv, '.');
218         if ( whichApp==TRUE && groupName )
219             *groupName++ = '\0';
220         else
221             groupName = *argv;
222         grp = getgrnam(groupName);
223         if (grp == NULL) {
224             fprintf(stderr, "%s: Unknown group name: %s\n", invocationName, groupName);
225             exit( FALSE);
226         }
227         gid = grp->gr_gid;
228
229         /* Find the selected user (if appropriate)  */
230         if (whichApp==TRUE) {
231             pwd = getpwnam(*argv);
232             if (pwd == NULL) {
233                 fprintf(stderr, "%s: Unknown user name: %s\n", invocationName, *argv);
234                 exit( FALSE);
235             }
236             uid = pwd->pw_uid;
237         }
238     }
239     
240     /* Ok, ready to do the deed now */
241     if (argc <= 1) {
242         fprintf(stderr, "%s: too few arguments", invocationName);
243         exit( FALSE);
244     }
245     while (argc-- > 1) {
246         argv++;
247         recursiveAction( *argv, recursiveFlag, TRUE, fileAction, fileAction);
248     }
249     exit(TRUE);
250 }
251