Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/lib/libcmd/chgrp.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the ast package *
4
* Copyright (c) 1992-2012 AT&T Intellectual Property *
5
* and is licensed under the *
6
* Eclipse Public License, Version 1.0 *
7
* by AT&T Intellectual Property *
8
* *
9
* A copy of the License is available at *
10
* http://www.eclipse.org/org/documents/epl-v10.html *
11
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12
* *
13
* Information and Software Systems Research *
14
* AT&T Research *
15
* Florham Park NJ *
16
* *
17
* Glenn Fowler <[email protected]> *
18
* David Korn <[email protected]> *
19
* *
20
***********************************************************************/
21
#pragma prototyped
22
/*
23
* David Korn
24
* Glenn Fowler
25
* AT&T Research
26
*
27
* chgrp+chown
28
*/
29
30
static const char usage_1[] =
31
"[-?@(#)$Id: chgrp (AT&T Research) 2012-04-20 $\n]"
32
USAGE_LICENSE
33
;
34
35
static const char usage_grp_1[] =
36
"[+NAME?chgrp - change the group ownership of files]"
37
"[+DESCRIPTION?\bchgrp\b changes the group ownership of each file"
38
" to \agroup\a, which can be either a group name or a numeric"
39
" group id. The user ownership of each file may also be changed to"
40
" \auser\a by prepending \auser\a\b:\b to the group name.]"
41
;
42
43
static const char usage_own_1[] =
44
"[+NAME?chown - change the ownership of files]"
45
"[+DESCRIPTION?\bchown\b changes the ownership of each file"
46
" to \auser\a, which can be either a user name or a numeric"
47
" user id. The group ownership of each file may also be changed to"
48
" \auser\a by appending \b:\b\agroup\a to the user name.]"
49
;
50
51
static const char usage_2[] =
52
"[b:before?Only change files with \bctime\b before (less than) the "
53
"\bmtime\b of \afile\a.]:[file]"
54
"[c:changes?Describe only files whose ownership actually changes.]"
55
"[f:quiet|silent?Do not report files whose ownership fails to change.]"
56
"[h|l:symlink?Change the ownership of symbolic links on systems that "
57
"support \blchown\b(2). Implies \b--physical\b.]"
58
"[m:map?The first operand is interpreted as a file that contains a map "
59
"of space separated \afrom_uid:from_gid to_uid:to_gid\a pairs. The "
60
"\auid\a or \agid\a part of each pair may be omitted to mean any \auid\a "
61
"or \agid\a. Ownership of files matching the \afrom\a part of any pair "
62
"is changed to the corresponding \ato\a part of the pair. The matching "
63
"for each file operand is in the order \auid\a:\agid\a, \auid\a:, "
64
":\agid\a. For a given file, once a \auid\a or \agid\a mapping is "
65
"determined it is not overridden by any subsequent match. Unmatched "
66
"files are silently ignored.]"
67
"[n:show?Show actions but don't execute.]"
68
"[N:numeric?By default numeric user and group id operands are first "
69
"interpreted as names; if no name exists then they are interpreted as "
70
"explicit numeric ids. \b--numeric\b interprets numeric id operands as "
71
"numeric ids.]"
72
"[r:reference?Omit the explicit ownership operand and use the ownership "
73
"of \afile\a instead.]:[file]"
74
"[u:unmapped?Print a diagnostic for each file for which either the "
75
"\auid\a or \agid\a or both were not mapped.]"
76
"[v:verbose?Describe changed permissions of all files.]"
77
"[H:metaphysical?Follow symbolic links for command arguments; otherwise "
78
"don't follow symbolic links when traversing directories.]"
79
"[L:logical|follow?Follow symbolic links when traversing directories.]"
80
"[P:physical|nofollow?Don't follow symbolic links when traversing "
81
"directories.]"
82
"[R:recursive?Recursively change ownership of directories and their "
83
"contents.]"
84
"[X:test?Canonicalize output for testing.]"
85
86
"\n"
87
"\n"
88
;
89
90
static const char usage_3[] =
91
" file ...\n"
92
"\n"
93
"[+EXIT STATUS?]{"
94
"[+0?All files changed successfully.]"
95
"[+>0?Unable to change ownership of one or more files.]"
96
"}"
97
"[+SEE ALSO?\bchmod\b(1), \bchown\b(2), \btw\b(1), \bgetconf\b(1), \bls\b(1)]"
98
;
99
100
#if defined(__STDPP__directive) && defined(__STDPP__hide)
101
__STDPP__directive pragma pp:hide lchown
102
#else
103
#define lchown ______lchown
104
#endif
105
106
#include <cmd.h>
107
#include <cdt.h>
108
#include <ls.h>
109
#include <ctype.h>
110
#include <fts_fix.h>
111
112
#ifndef ENOSYS
113
#define ENOSYS EINVAL
114
#endif
115
116
#include "FEATURE/symlink"
117
118
#if defined(__STDPP__directive) && defined(__STDPP__hide)
119
__STDPP__directive pragma pp:nohide lchown
120
#else
121
#undef lchown
122
#endif
123
124
typedef struct Key_s /* uid/gid key */
125
{
126
int uid; /* uid */
127
int gid; /* gid */
128
} Key_t;
129
130
typedef struct Map_s /* uid/gid map */
131
{
132
Dtlink_t link; /* dictionary link */
133
Key_t key; /* key */
134
Key_t to; /* map to these */
135
} Map_t;
136
137
#define NOID (-1)
138
139
#define OPT_CHOWN 0x0001 /* chown */
140
#define OPT_FORCE 0x0002 /* ignore errors */
141
#define OPT_GID 0x0004 /* have gid */
142
#define OPT_LCHOWN 0x0008 /* lchown */
143
#define OPT_NUMERIC 0x0010 /* favor numeric ids */
144
#define OPT_SHOW 0x0020 /* show but don't do */
145
#define OPT_TEST 0x0040 /* canonicalize output */
146
#define OPT_UID 0x0080 /* have uid */
147
#define OPT_UNMAPPED 0x0100 /* unmapped file diagnostic */
148
#define OPT_VERBOSE 0x0200 /* have uid */
149
150
extern int lchown(const char*, uid_t, gid_t);
151
152
/*
153
* parse uid and gid from s
154
*/
155
156
static void
157
getids(register char* s, char** e, Key_t* key, int options)
158
{
159
register char* t;
160
register int n;
161
register int m;
162
char* z;
163
char buf[64];
164
165
key->uid = key->gid = NOID;
166
while (isspace(*s))
167
s++;
168
for (t = s; (n = *t) && n != ':' && n != '.' && !isspace(n); t++);
169
if (n)
170
{
171
options |= OPT_CHOWN;
172
if ((n = t++ - s) >= sizeof(buf))
173
n = sizeof(buf) - 1;
174
*((s = (char*)memcpy(buf, s, n)) + n) = 0;
175
}
176
if (options & OPT_CHOWN)
177
{
178
if (*s)
179
{
180
n = (int)strtol(s, &z, 0);
181
if (*z || !(options & OPT_NUMERIC))
182
{
183
if ((m = struid(s)) != NOID)
184
n = m;
185
else if (*z)
186
error(ERROR_exit(1), "%s: unknown user", s);
187
}
188
key->uid = n;
189
}
190
for (s = t; (n = *t) && !isspace(n); t++);
191
if (n)
192
{
193
if ((n = t++ - s) >= sizeof(buf))
194
n = sizeof(buf) - 1;
195
*((s = (char*)memcpy(buf, s, n)) + n) = 0;
196
}
197
}
198
if (*s)
199
{
200
n = (int)strtol(s, &z, 0);
201
if (*z || !(options & OPT_NUMERIC))
202
{
203
if ((m = strgid(s)) != NOID)
204
n = m;
205
else if (*z)
206
error(ERROR_exit(1), "%s: unknown group", s);
207
}
208
key->gid = n;
209
}
210
if (e)
211
*e = t;
212
}
213
214
/*
215
* NOTE: we only use the native lchown() on symlinks just in case
216
* the implementation is a feckless stub
217
*/
218
219
int
220
b_chgrp(int argc, char** argv, Shbltin_t* context)
221
{
222
register int options = 0;
223
register char* s;
224
register Map_t* m;
225
register FTS* fts;
226
register FTSENT*ent;
227
register int i;
228
Dt_t* map = 0;
229
int logical = 1;
230
int flags;
231
int uid;
232
int gid;
233
char* op;
234
char* usage;
235
char* t;
236
Sfio_t* sp;
237
unsigned long before;
238
Dtdisc_t mapdisc;
239
Key_t keys[3];
240
Key_t key;
241
struct stat st;
242
int (*chownf)(const char*, uid_t, gid_t);
243
244
cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
245
flags = fts_flags() | FTS_META | FTS_TOP | FTS_NOPOSTORDER | FTS_NOSEEDOTDIR;
246
before = ~0;
247
if (!(sp = sfstropen()))
248
error(ERROR_SYSTEM|3, "out of space");
249
sfputr(sp, usage_1, -1);
250
if (error_info.id[2] == 'g')
251
sfputr(sp, usage_grp_1, -1);
252
else
253
{
254
sfputr(sp, usage_own_1, -1);
255
options |= OPT_CHOWN;
256
}
257
sfputr(sp, usage_2, -1);
258
if (options & OPT_CHOWN)
259
sfputr(sp, ERROR_translate(0, 0, 0, "[owner[:group]]"), -1);
260
else
261
sfputr(sp, ERROR_translate(0, 0, 0, "[[owner:]group]"), -1);
262
sfputr(sp, usage_3, -1);
263
if (!(usage = sfstruse(sp)))
264
error(ERROR_SYSTEM|3, "out of space");
265
for (;;)
266
{
267
switch (optget(argv, usage))
268
{
269
case 'b':
270
if (stat(opt_info.arg, &st))
271
error(ERROR_exit(1), "%s: cannot stat", opt_info.arg);
272
before = st.st_mtime;
273
continue;
274
case 'c':
275
case 'v':
276
options |= OPT_VERBOSE;
277
continue;
278
case 'f':
279
options |= OPT_FORCE;
280
continue;
281
case 'h':
282
options |= OPT_LCHOWN;
283
continue;
284
case 'm':
285
memset(&mapdisc, 0, sizeof(mapdisc));
286
mapdisc.key = offsetof(Map_t, key);
287
mapdisc.size = sizeof(Key_t);
288
if (!(map = dtopen(&mapdisc, Dtset)))
289
error(ERROR_exit(1), "out of space [id map]");
290
continue;
291
case 'n':
292
options |= OPT_SHOW;
293
continue;
294
case 'N':
295
options |= OPT_NUMERIC;
296
continue;
297
case 'r':
298
if (stat(opt_info.arg, &st))
299
error(ERROR_exit(1), "%s: cannot stat", opt_info.arg);
300
uid = st.st_uid;
301
gid = st.st_gid;
302
options |= OPT_UID|OPT_GID;
303
continue;
304
case 'u':
305
options |= OPT_UNMAPPED;
306
continue;
307
case 'H':
308
flags |= FTS_META|FTS_PHYSICAL;
309
logical = 0;
310
continue;
311
case 'L':
312
flags &= ~(FTS_META|FTS_PHYSICAL);
313
logical = 0;
314
continue;
315
case 'P':
316
flags &= ~FTS_META;
317
flags |= FTS_PHYSICAL;
318
logical = 0;
319
continue;
320
case 'R':
321
flags &= ~FTS_TOP;
322
logical = 0;
323
continue;
324
case 'X':
325
options |= OPT_TEST;
326
continue;
327
case ':':
328
error(2, "%s", opt_info.arg);
329
continue;
330
case '?':
331
error(ERROR_usage(2), "%s", opt_info.arg);
332
break;
333
}
334
break;
335
}
336
argv += opt_info.index;
337
argc -= opt_info.index;
338
if (error_info.errors || argc < 2)
339
error(ERROR_usage(2), "%s", optusage(NiL));
340
s = *argv;
341
if (options & OPT_LCHOWN)
342
{
343
flags &= ~FTS_META;
344
flags |= FTS_PHYSICAL;
345
logical = 0;
346
}
347
if (logical)
348
flags &= ~(FTS_META|FTS_PHYSICAL);
349
if (map)
350
{
351
if (streq(s, "-"))
352
sp = sfstdin;
353
else if (!(sp = sfopen(NiL, s, "r")))
354
error(ERROR_exit(1), "%s: cannot read", s);
355
while (s = sfgetr(sp, '\n', 1))
356
{
357
getids(s, &t, &key, options);
358
if (!(m = (Map_t*)dtmatch(map, &key)))
359
{
360
if (!(m = (Map_t*)stakalloc(sizeof(Map_t))))
361
error(ERROR_exit(1), "out of space [id dictionary]");
362
m->key = key;
363
m->to.uid = m->to.gid = NOID;
364
dtinsert(map, m);
365
}
366
getids(t, NiL, &m->to, options);
367
}
368
if (sp != sfstdin)
369
sfclose(sp);
370
keys[1].gid = keys[2].uid = NOID;
371
}
372
else if (!(options & (OPT_UID|OPT_GID)))
373
{
374
getids(s, NiL, &key, options);
375
if ((uid = key.uid) != NOID)
376
options |= OPT_UID;
377
if ((gid = key.gid) != NOID)
378
options |= OPT_GID;
379
}
380
switch (options & (OPT_UID|OPT_GID))
381
{
382
case OPT_UID:
383
s = ERROR_translate(0, 0, 0, " owner");
384
break;
385
case OPT_GID:
386
s = ERROR_translate(0, 0, 0, " group");
387
break;
388
case OPT_UID|OPT_GID:
389
s = ERROR_translate(0, 0, 0, " owner and group");
390
break;
391
default:
392
s = "";
393
break;
394
}
395
if (!(fts = fts_open(argv + 1, flags, NiL)))
396
error(ERROR_system(1), "%s: not found", argv[1]);
397
while (!sh_checksig(context) && (ent = fts_read(fts)))
398
switch (ent->fts_info)
399
{
400
case FTS_SL:
401
case FTS_SLNONE:
402
if (options & OPT_LCHOWN)
403
{
404
#if _lib_lchown
405
chownf = lchown;
406
op = "lchown";
407
goto commit;
408
#else
409
if (!(options & OPT_FORCE))
410
{
411
errno = ENOSYS;
412
error(ERROR_system(0), "%s: cannot change symlink owner/group", ent->fts_path);
413
}
414
#endif
415
}
416
break;
417
case FTS_F:
418
case FTS_D:
419
anyway:
420
chownf = chown;
421
op = "chown";
422
commit:
423
if ((unsigned long)ent->fts_statp->st_ctime >= before)
424
break;
425
if (map)
426
{
427
options &= ~(OPT_UID|OPT_GID);
428
uid = gid = NOID;
429
keys[0].uid = keys[1].uid = ent->fts_statp->st_uid;
430
keys[0].gid = keys[2].gid = ent->fts_statp->st_gid;
431
i = 0;
432
do
433
{
434
if (m = (Map_t*)dtmatch(map, &keys[i]))
435
{
436
if (uid == NOID && m->to.uid != NOID)
437
{
438
uid = m->to.uid;
439
options |= OPT_UID;
440
}
441
if (gid == NOID && m->to.gid != NOID)
442
{
443
gid = m->to.gid;
444
options |= OPT_GID;
445
}
446
}
447
} while (++i < elementsof(keys) && (uid == NOID || gid == NOID));
448
}
449
else
450
{
451
if (!(options & OPT_UID))
452
uid = ent->fts_statp->st_uid;
453
if (!(options & OPT_GID))
454
gid = ent->fts_statp->st_gid;
455
}
456
if ((options & OPT_UNMAPPED) && (uid == NOID || gid == NOID))
457
{
458
if (uid == NOID && gid == NOID)
459
error(ERROR_warn(0), "%s: uid and gid not mapped", ent->fts_path);
460
else if (uid == NOID)
461
error(ERROR_warn(0), "%s: uid not mapped", ent->fts_path);
462
else
463
error(ERROR_warn(0), "%s: gid not mapped", ent->fts_path);
464
}
465
if (uid != ent->fts_statp->st_uid && uid != NOID || gid != ent->fts_statp->st_gid && gid != NOID)
466
{
467
if (options & (OPT_SHOW|OPT_VERBOSE))
468
{
469
if (options & OPT_TEST)
470
{
471
ent->fts_statp->st_uid = 0;
472
ent->fts_statp->st_gid = 0;
473
}
474
sfprintf(sfstdout, "%s uid:%05d->%05d gid:%05d->%05d %s\n", op, ent->fts_statp->st_uid, uid, ent->fts_statp->st_gid, gid, ent->fts_path);
475
}
476
if (!(options & OPT_SHOW) && (*chownf)(ent->fts_accpath, uid, gid) && !(options & OPT_FORCE))
477
error(ERROR_system(0), "%s: cannot change%s", ent->fts_path, s);
478
}
479
break;
480
case FTS_DC:
481
if (!(options & OPT_FORCE))
482
error(ERROR_warn(0), "%s: directory causes cycle", ent->fts_path);
483
break;
484
case FTS_DNR:
485
if (!(options & OPT_FORCE))
486
error(ERROR_system(0), "%s: cannot read directory", ent->fts_path);
487
goto anyway;
488
case FTS_DNX:
489
if (!(options & OPT_FORCE))
490
error(ERROR_system(0), "%s: cannot search directory", ent->fts_path);
491
goto anyway;
492
case FTS_NS:
493
if (!(options & OPT_FORCE))
494
error(ERROR_system(0), "%s: not found", ent->fts_path);
495
break;
496
}
497
fts_close(fts);
498
if (map)
499
dtclose(map);
500
return error_info.errors != 0;
501
}
502
503