1da2e3ebdSchin /***********************************************************************
2da2e3ebdSchin * *
3da2e3ebdSchin * This software is part of the ast package *
4*3e14f97fSRoger A. Faulkner * Copyright (c) 1992-2010 AT&T Intellectual Property *
5da2e3ebdSchin * and is licensed under the *
6da2e3ebdSchin * Common Public License, Version 1.0 *
77c2fbfb3SApril Chin * by AT&T Intellectual Property *
8da2e3ebdSchin * *
9da2e3ebdSchin * A copy of the License is available at *
10da2e3ebdSchin * http://www.opensource.org/licenses/cpl1.0.txt *
11da2e3ebdSchin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
12da2e3ebdSchin * *
13da2e3ebdSchin * Information and Software Systems Research *
14da2e3ebdSchin * AT&T Research *
15da2e3ebdSchin * Florham Park NJ *
16da2e3ebdSchin * *
17da2e3ebdSchin * Glenn Fowler <gsf@research.att.com> *
18da2e3ebdSchin * David Korn <dgk@research.att.com> *
19da2e3ebdSchin * *
20da2e3ebdSchin ***********************************************************************/
21da2e3ebdSchin #pragma prototyped
22da2e3ebdSchin /*
23da2e3ebdSchin * David Korn
24da2e3ebdSchin * Glenn Fowler
25da2e3ebdSchin * AT&T Research
26da2e3ebdSchin *
27da2e3ebdSchin * chgrp+chown
28da2e3ebdSchin */
29da2e3ebdSchin
30da2e3ebdSchin static const char usage_1[] =
3134f9b3eeSRoland Mainz "[-?@(#)$Id: chgrp (AT&T Research) 2009-07-02 $\n]"
32da2e3ebdSchin USAGE_LICENSE
33da2e3ebdSchin ;
34da2e3ebdSchin
35da2e3ebdSchin static const char usage_grp_1[] =
36da2e3ebdSchin "[+NAME?chgrp - change the group ownership of files]"
37da2e3ebdSchin "[+DESCRIPTION?\bchgrp\b changes the group ownership of each file"
38da2e3ebdSchin " to \agroup\a, which can be either a group name or a numeric"
39da2e3ebdSchin " group id. The user ownership of each file may also be changed to"
40da2e3ebdSchin " \auser\a by prepending \auser\a\b:\b to the group name.]"
41da2e3ebdSchin ;
42da2e3ebdSchin
43da2e3ebdSchin static const char usage_own_1[] =
44da2e3ebdSchin "[+NAME?chown - change the ownership of files]"
45da2e3ebdSchin "[+DESCRIPTION?\bchown\b changes the ownership of each file"
46da2e3ebdSchin " to \auser\a, which can be either a user name or a numeric"
47da2e3ebdSchin " user id. The group ownership of each file may also be changed to"
48da2e3ebdSchin " \auser\a by appending \b:\b\agroup\a to the user name.]"
49da2e3ebdSchin ;
50da2e3ebdSchin
51da2e3ebdSchin static const char usage_2[] =
527c2fbfb3SApril Chin "[b:before?Only change files with \bctime\b before (less than) the "
537c2fbfb3SApril Chin "\bmtime\b of \afile\a.]:[file]"
54da2e3ebdSchin "[c:changes?Describe only files whose ownership actually changes.]"
55da2e3ebdSchin "[f:quiet|silent?Do not report files whose ownership fails to change.]"
56da2e3ebdSchin "[l|h:symlink?Change the ownership of the symbolic links on systems that "
57da2e3ebdSchin "support this.]"
58da2e3ebdSchin "[m:map?The first operand is interpreted as a file that contains a map "
597c2fbfb3SApril Chin "of space separated \afrom_uid:from_gid to_uid:to_gid\a pairs. The "
607c2fbfb3SApril Chin "\auid\a or \agid\a part of each pair may be omitted to mean any \auid\a "
617c2fbfb3SApril Chin "or \agid\a. Ownership of files matching the \afrom\a part of any pair "
627c2fbfb3SApril Chin "is changed to the corresponding \ato\a part of the pair. The matching "
637c2fbfb3SApril Chin "for each file operand is in the order \auid\a:\agid\a, \auid\a:, "
647c2fbfb3SApril Chin ":\agid\a. For a given file, once a \auid\a or \agid\a mapping is "
657c2fbfb3SApril Chin "determined it is not overridden by any subsequent match. Unmatched "
667c2fbfb3SApril Chin "files are silently ignored.]"
67da2e3ebdSchin "[n:show?Show actions but don't execute.]"
687c2fbfb3SApril Chin "[r:reference?Omit the explicit ownership operand and use the ownership "
697c2fbfb3SApril Chin "of \afile\a instead.]:[file]"
707c2fbfb3SApril Chin "[u:unmapped?Print a diagnostic for each file for which either the "
717c2fbfb3SApril Chin "\auid\a or \agid\a or both were not mapped.]"
72da2e3ebdSchin "[v:verbose?Describe changed permissions of all files.]"
737c2fbfb3SApril Chin "[H:metaphysical?Follow symbolic links for command arguments; otherwise "
747c2fbfb3SApril Chin "don't follow symbolic links when traversing directories.]"
75da2e3ebdSchin "[L:logical|follow?Follow symbolic links when traversing directories.]"
767c2fbfb3SApril Chin "[P:physical|nofollow?Don't follow symbolic links when traversing "
777c2fbfb3SApril Chin "directories.]"
787c2fbfb3SApril Chin "[R:recursive?Recursively change ownership of directories and their "
797c2fbfb3SApril Chin "contents.]"
80da2e3ebdSchin "[X:test?Canonicalize output for testing.]"
81da2e3ebdSchin
82da2e3ebdSchin "\n"
83da2e3ebdSchin "\n"
84da2e3ebdSchin ;
85da2e3ebdSchin
86da2e3ebdSchin static const char usage_3[] =
87da2e3ebdSchin " file ...\n"
88da2e3ebdSchin "\n"
89da2e3ebdSchin "[+EXIT STATUS?]{"
90da2e3ebdSchin "[+0?All files changed successfully.]"
91da2e3ebdSchin "[+>0?Unable to change ownership of one or more files.]"
92da2e3ebdSchin "}"
93da2e3ebdSchin "[+SEE ALSO?\bchmod\b(1), \btw\b(1), \bgetconf\b(1), \bls\b(1)]"
94da2e3ebdSchin ;
95da2e3ebdSchin
96da2e3ebdSchin #if defined(__STDPP__directive) && defined(__STDPP__hide)
97da2e3ebdSchin __STDPP__directive pragma pp:hide lchown
98da2e3ebdSchin #else
99da2e3ebdSchin #define lchown ______lchown
100da2e3ebdSchin #endif
101da2e3ebdSchin
102da2e3ebdSchin #include <cmd.h>
103da2e3ebdSchin #include <cdt.h>
104da2e3ebdSchin #include <ls.h>
105da2e3ebdSchin #include <ctype.h>
106*3e14f97fSRoger A. Faulkner #include <fts_fix.h>
107da2e3ebdSchin
108da2e3ebdSchin #include "FEATURE/symlink"
109da2e3ebdSchin
110da2e3ebdSchin #if defined(__STDPP__directive) && defined(__STDPP__hide)
111da2e3ebdSchin __STDPP__directive pragma pp:nohide lchown
112da2e3ebdSchin #else
113da2e3ebdSchin #undef lchown
114da2e3ebdSchin #endif
115da2e3ebdSchin
1167c2fbfb3SApril Chin typedef struct Key_s /* uid/gid key */
1177c2fbfb3SApril Chin {
1187c2fbfb3SApril Chin int uid; /* uid */
1197c2fbfb3SApril Chin int gid; /* gid */
1207c2fbfb3SApril Chin } Key_t;
1217c2fbfb3SApril Chin
1227c2fbfb3SApril Chin typedef struct Map_s /* uid/gid map */
123da2e3ebdSchin {
124da2e3ebdSchin Dtlink_t link; /* dictionary link */
1257c2fbfb3SApril Chin Key_t key; /* key */
1267c2fbfb3SApril Chin Key_t to; /* map to these */
127da2e3ebdSchin } Map_t;
128da2e3ebdSchin
129da2e3ebdSchin #define NOID (-1)
130da2e3ebdSchin
131da2e3ebdSchin #define OPT_CHOWN (1<<0) /* chown */
132da2e3ebdSchin #define OPT_FORCE (1<<1) /* ignore errors */
133da2e3ebdSchin #define OPT_GID (1<<2) /* have gid */
134da2e3ebdSchin #define OPT_LCHOWN (1<<3) /* lchown */
135da2e3ebdSchin #define OPT_SHOW (1<<4) /* show but don't do */
136da2e3ebdSchin #define OPT_TEST (1<<5) /* canonicalize output */
137da2e3ebdSchin #define OPT_UID (1<<6) /* have uid */
1387c2fbfb3SApril Chin #define OPT_UNMAPPED (1<<7) /* unmapped file diagnostic */
1397c2fbfb3SApril Chin #define OPT_VERBOSE (1<<8) /* have uid */
140da2e3ebdSchin
141da2e3ebdSchin extern int lchown(const char*, uid_t, gid_t);
142da2e3ebdSchin
143da2e3ebdSchin #if !_lib_lchown
144da2e3ebdSchin
145da2e3ebdSchin #ifndef ENOSYS
146da2e3ebdSchin #define ENOSYS EINVAL
147da2e3ebdSchin #endif
148da2e3ebdSchin
149da2e3ebdSchin int
lchown(const char * path,uid_t uid,gid_t gid)150da2e3ebdSchin lchown(const char* path, uid_t uid, gid_t gid)
151da2e3ebdSchin {
152da2e3ebdSchin return ENOSYS;
153da2e3ebdSchin }
154da2e3ebdSchin
155da2e3ebdSchin #endif /* _lib_chown */
156da2e3ebdSchin
157da2e3ebdSchin /*
158da2e3ebdSchin * parse uid and gid from s
159da2e3ebdSchin */
160da2e3ebdSchin
161da2e3ebdSchin static void
getids(register char * s,char ** e,Key_t * key,int options)1627c2fbfb3SApril Chin getids(register char* s, char** e, Key_t* key, int options)
163da2e3ebdSchin {
164da2e3ebdSchin register char* t;
165da2e3ebdSchin register int n;
166da2e3ebdSchin char* z;
167da2e3ebdSchin char buf[64];
168da2e3ebdSchin
1697c2fbfb3SApril Chin key->uid = key->gid = NOID;
170da2e3ebdSchin while (isspace(*s))
171da2e3ebdSchin s++;
172da2e3ebdSchin for (t = s; (n = *t) && n != ':' && n != '.' && !isspace(n); t++);
173da2e3ebdSchin if (n)
174da2e3ebdSchin {
175da2e3ebdSchin options |= OPT_CHOWN;
176da2e3ebdSchin if ((n = t++ - s) >= sizeof(buf))
177da2e3ebdSchin n = sizeof(buf) - 1;
178da2e3ebdSchin *((s = (char*)memcpy(buf, s, n)) + n) = 0;
179da2e3ebdSchin }
180da2e3ebdSchin if (options & OPT_CHOWN)
181da2e3ebdSchin {
182da2e3ebdSchin if (*s)
183da2e3ebdSchin {
184da2e3ebdSchin if ((n = struid(s)) == NOID)
185da2e3ebdSchin {
186da2e3ebdSchin n = (int)strtol(s, &z, 0);
187da2e3ebdSchin if (*z)
188da2e3ebdSchin error(ERROR_exit(1), "%s: unknown user", s);
189da2e3ebdSchin }
1907c2fbfb3SApril Chin key->uid = n;
191da2e3ebdSchin }
192da2e3ebdSchin for (s = t; (n = *t) && !isspace(n); t++);
193da2e3ebdSchin if (n)
194da2e3ebdSchin {
195da2e3ebdSchin if ((n = t++ - s) >= sizeof(buf))
196da2e3ebdSchin n = sizeof(buf) - 1;
197da2e3ebdSchin *((s = (char*)memcpy(buf, s, n)) + n) = 0;
198da2e3ebdSchin }
199da2e3ebdSchin }
200da2e3ebdSchin if (*s)
201da2e3ebdSchin {
202da2e3ebdSchin if ((n = strgid(s)) == NOID)
203da2e3ebdSchin {
204da2e3ebdSchin n = (int)strtol(s, &z, 0);
205da2e3ebdSchin if (*z)
206da2e3ebdSchin error(ERROR_exit(1), "%s: unknown group", s);
207da2e3ebdSchin }
2087c2fbfb3SApril Chin key->gid = n;
209da2e3ebdSchin }
210da2e3ebdSchin if (e)
211da2e3ebdSchin *e = t;
212da2e3ebdSchin }
213da2e3ebdSchin
214da2e3ebdSchin int
b_chgrp(int argc,char ** argv,void * context)215da2e3ebdSchin b_chgrp(int argc, char** argv, void* context)
216da2e3ebdSchin {
217da2e3ebdSchin register int options = 0;
218da2e3ebdSchin register char* s;
219da2e3ebdSchin register Map_t* m;
220da2e3ebdSchin register FTS* fts;
221da2e3ebdSchin register FTSENT*ent;
2227c2fbfb3SApril Chin register int i;
223da2e3ebdSchin Dt_t* map = 0;
22434f9b3eeSRoland Mainz int logical = 1;
225da2e3ebdSchin int flags;
226da2e3ebdSchin int uid;
227da2e3ebdSchin int gid;
228da2e3ebdSchin char* op;
229da2e3ebdSchin char* usage;
2307c2fbfb3SApril Chin char* t;
231da2e3ebdSchin Sfio_t* sp;
2327c2fbfb3SApril Chin unsigned long before;
233da2e3ebdSchin Dtdisc_t mapdisc;
2347c2fbfb3SApril Chin Key_t keys[3];
2357c2fbfb3SApril Chin Key_t key;
236da2e3ebdSchin struct stat st;
237da2e3ebdSchin int (*chownf)(const char*, uid_t, gid_t);
238da2e3ebdSchin
239da2e3ebdSchin cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
240da2e3ebdSchin flags = fts_flags() | FTS_TOP | FTS_NOPOSTORDER | FTS_NOSEEDOTDIR;
2417c2fbfb3SApril Chin before = ~0;
242da2e3ebdSchin if (!(sp = sfstropen()))
243da2e3ebdSchin error(ERROR_SYSTEM|3, "out of space");
244da2e3ebdSchin sfputr(sp, usage_1, -1);
245da2e3ebdSchin if (error_info.id[2] == 'g')
246da2e3ebdSchin sfputr(sp, usage_grp_1, -1);
247da2e3ebdSchin else
248da2e3ebdSchin {
249da2e3ebdSchin sfputr(sp, usage_own_1, -1);
250da2e3ebdSchin options |= OPT_CHOWN;
251da2e3ebdSchin }
252da2e3ebdSchin sfputr(sp, usage_2, -1);
253da2e3ebdSchin if (options & OPT_CHOWN)
254da2e3ebdSchin sfputr(sp, ERROR_translate(0, 0, 0, "[owner[:group]]"), -1);
255da2e3ebdSchin else
256da2e3ebdSchin sfputr(sp, ERROR_translate(0, 0, 0, "[[owner:]group]"), -1);
257da2e3ebdSchin sfputr(sp, usage_3, -1);
258da2e3ebdSchin if (!(usage = sfstruse(sp)))
259da2e3ebdSchin error(ERROR_SYSTEM|3, "out of space");
260da2e3ebdSchin for (;;)
261da2e3ebdSchin {
262da2e3ebdSchin switch (optget(argv, usage))
263da2e3ebdSchin {
2647c2fbfb3SApril Chin case 'b':
2657c2fbfb3SApril Chin if (stat(opt_info.arg, &st))
2667c2fbfb3SApril Chin error(ERROR_exit(1), "%s: cannot stat", opt_info.arg);
2677c2fbfb3SApril Chin before = st.st_mtime;
2687c2fbfb3SApril Chin continue;
269da2e3ebdSchin case 'c':
270da2e3ebdSchin case 'v':
271da2e3ebdSchin options |= OPT_VERBOSE;
272da2e3ebdSchin continue;
273da2e3ebdSchin case 'f':
274da2e3ebdSchin options |= OPT_FORCE;
275da2e3ebdSchin continue;
276da2e3ebdSchin case 'l':
277da2e3ebdSchin options |= OPT_LCHOWN;
278da2e3ebdSchin continue;
279da2e3ebdSchin case 'm':
280da2e3ebdSchin memset(&mapdisc, 0, sizeof(mapdisc));
2817c2fbfb3SApril Chin mapdisc.key = offsetof(Map_t, key);
2827c2fbfb3SApril Chin mapdisc.size = sizeof(Key_t);
283da2e3ebdSchin if (!(map = dtopen(&mapdisc, Dthash)))
284da2e3ebdSchin error(ERROR_exit(1), "out of space [id map]");
285da2e3ebdSchin continue;
286da2e3ebdSchin case 'n':
287da2e3ebdSchin options |= OPT_SHOW;
288da2e3ebdSchin continue;
289da2e3ebdSchin case 'r':
290da2e3ebdSchin if (stat(opt_info.arg, &st))
291da2e3ebdSchin error(ERROR_exit(1), "%s: cannot stat", opt_info.arg);
292da2e3ebdSchin uid = st.st_uid;
293da2e3ebdSchin gid = st.st_gid;
294da2e3ebdSchin options |= OPT_UID|OPT_GID;
295da2e3ebdSchin continue;
2967c2fbfb3SApril Chin case 'u':
2977c2fbfb3SApril Chin options |= OPT_UNMAPPED;
2987c2fbfb3SApril Chin continue;
299da2e3ebdSchin case 'H':
300da2e3ebdSchin flags |= FTS_META|FTS_PHYSICAL;
30134f9b3eeSRoland Mainz logical = 0;
302da2e3ebdSchin continue;
303da2e3ebdSchin case 'L':
304da2e3ebdSchin flags &= ~(FTS_META|FTS_PHYSICAL);
30534f9b3eeSRoland Mainz logical = 0;
306da2e3ebdSchin continue;
307da2e3ebdSchin case 'P':
308da2e3ebdSchin flags &= ~FTS_META;
309da2e3ebdSchin flags |= FTS_PHYSICAL;
31034f9b3eeSRoland Mainz logical = 0;
311da2e3ebdSchin continue;
312da2e3ebdSchin case 'R':
313da2e3ebdSchin flags &= ~FTS_TOP;
31434f9b3eeSRoland Mainz logical = 0;
315da2e3ebdSchin continue;
316da2e3ebdSchin case 'X':
317da2e3ebdSchin options |= OPT_TEST;
318da2e3ebdSchin continue;
319da2e3ebdSchin case ':':
320da2e3ebdSchin error(2, "%s", opt_info.arg);
321da2e3ebdSchin continue;
322da2e3ebdSchin case '?':
323da2e3ebdSchin error(ERROR_usage(2), "%s", opt_info.arg);
324da2e3ebdSchin break;
325da2e3ebdSchin }
326da2e3ebdSchin break;
327da2e3ebdSchin }
328da2e3ebdSchin argv += opt_info.index;
329da2e3ebdSchin argc -= opt_info.index;
330da2e3ebdSchin if (error_info.errors || argc < 2)
331da2e3ebdSchin error(ERROR_usage(2), "%s", optusage(NiL));
332da2e3ebdSchin s = *argv;
33334f9b3eeSRoland Mainz if (logical)
33434f9b3eeSRoland Mainz flags &= ~(FTS_META|FTS_PHYSICAL);
335da2e3ebdSchin if (map)
336da2e3ebdSchin {
337da2e3ebdSchin if (streq(s, "-"))
338da2e3ebdSchin sp = sfstdin;
339da2e3ebdSchin else if (!(sp = sfopen(NiL, s, "r")))
340da2e3ebdSchin error(ERROR_exit(1), "%s: cannot read", s);
341da2e3ebdSchin while (s = sfgetr(sp, '\n', 1))
342da2e3ebdSchin {
3437c2fbfb3SApril Chin getids(s, &t, &key, options);
3447c2fbfb3SApril Chin if (!(m = (Map_t*)dtmatch(map, &key)))
345da2e3ebdSchin {
3467c2fbfb3SApril Chin if (!(m = (Map_t*)stakalloc(sizeof(Map_t))))
3477c2fbfb3SApril Chin error(ERROR_exit(1), "out of space [id dictionary]");
3487c2fbfb3SApril Chin m->key = key;
3497c2fbfb3SApril Chin m->to.uid = m->to.gid = NOID;
350da2e3ebdSchin dtinsert(map, m);
351da2e3ebdSchin }
3527c2fbfb3SApril Chin getids(t, NiL, &m->to, options);
353da2e3ebdSchin }
354da2e3ebdSchin if (sp != sfstdin)
355da2e3ebdSchin sfclose(sp);
3567c2fbfb3SApril Chin keys[1].gid = keys[2].uid = NOID;
357da2e3ebdSchin }
358da2e3ebdSchin else if (!(options & (OPT_UID|OPT_GID)))
359da2e3ebdSchin {
3607c2fbfb3SApril Chin getids(s, NiL, &key, options);
3617c2fbfb3SApril Chin if ((uid = key.uid) != NOID)
362da2e3ebdSchin options |= OPT_UID;
3637c2fbfb3SApril Chin if ((gid = key.gid) != NOID)
364da2e3ebdSchin options |= OPT_GID;
365da2e3ebdSchin }
366da2e3ebdSchin switch (options & (OPT_UID|OPT_GID))
367da2e3ebdSchin {
368da2e3ebdSchin case OPT_UID:
369da2e3ebdSchin s = ERROR_translate(0, 0, 0, " owner");
370da2e3ebdSchin break;
371da2e3ebdSchin case OPT_GID:
372da2e3ebdSchin s = ERROR_translate(0, 0, 0, " group");
373da2e3ebdSchin break;
374da2e3ebdSchin case OPT_UID|OPT_GID:
375da2e3ebdSchin s = ERROR_translate(0, 0, 0, " owner and group");
376da2e3ebdSchin break;
377da2e3ebdSchin default:
378da2e3ebdSchin s = "";
379da2e3ebdSchin break;
380da2e3ebdSchin }
381da2e3ebdSchin if (!(fts = fts_open(argv + 1, flags, NiL)))
382da2e3ebdSchin error(ERROR_system(1), "%s: not found", argv[1]);
3837c2fbfb3SApril Chin while (!sh_checksig(context) && (ent = fts_read(fts)))
384da2e3ebdSchin switch (ent->fts_info)
385da2e3ebdSchin {
386da2e3ebdSchin case FTS_F:
387da2e3ebdSchin case FTS_D:
388da2e3ebdSchin case FTS_SL:
389da2e3ebdSchin case FTS_SLNONE:
390da2e3ebdSchin anyway:
3917c2fbfb3SApril Chin if ((unsigned long)ent->fts_statp->st_ctime >= before)
3927c2fbfb3SApril Chin break;
393da2e3ebdSchin if (map)
394da2e3ebdSchin {
395da2e3ebdSchin options &= ~(OPT_UID|OPT_GID);
3967c2fbfb3SApril Chin uid = gid = NOID;
3977c2fbfb3SApril Chin keys[0].uid = keys[1].uid = ent->fts_statp->st_uid;
3987c2fbfb3SApril Chin keys[0].gid = keys[2].gid = ent->fts_statp->st_gid;
3997c2fbfb3SApril Chin i = 0;
4007c2fbfb3SApril Chin do
401da2e3ebdSchin {
4027c2fbfb3SApril Chin if (m = (Map_t*)dtmatch(map, &keys[i]))
4037c2fbfb3SApril Chin {
4047c2fbfb3SApril Chin if (uid == NOID && m->to.uid != NOID)
4057c2fbfb3SApril Chin {
4067c2fbfb3SApril Chin uid = m->to.uid;
407da2e3ebdSchin options |= OPT_UID;
408da2e3ebdSchin }
4097c2fbfb3SApril Chin if (gid == NOID && m->to.gid != NOID)
410da2e3ebdSchin {
4117c2fbfb3SApril Chin gid = m->to.gid;
412da2e3ebdSchin options |= OPT_GID;
413da2e3ebdSchin }
414da2e3ebdSchin }
4157c2fbfb3SApril Chin } while (++i < elementsof(keys) && (uid == NOID || gid == NOID));
4167c2fbfb3SApril Chin }
417da2e3ebdSchin else
418da2e3ebdSchin {
419da2e3ebdSchin if (!(options & OPT_UID))
420da2e3ebdSchin uid = ent->fts_statp->st_uid;
421da2e3ebdSchin if (!(options & OPT_GID))
422da2e3ebdSchin gid = ent->fts_statp->st_gid;
423da2e3ebdSchin }
4247c2fbfb3SApril Chin if ((options & OPT_UNMAPPED) && (uid == NOID || gid == NOID))
4257c2fbfb3SApril Chin {
4267c2fbfb3SApril Chin if (uid == NOID && gid == NOID)
4277c2fbfb3SApril Chin error(ERROR_warn(0), "%s: uid and gid not mapped", ent->fts_path);
4287c2fbfb3SApril Chin else if (uid == NOID)
4297c2fbfb3SApril Chin error(ERROR_warn(0), "%s: uid not mapped", ent->fts_path);
4307c2fbfb3SApril Chin else
4317c2fbfb3SApril Chin error(ERROR_warn(0), "%s: gid not mapped", ent->fts_path);
4327c2fbfb3SApril Chin }
4337c2fbfb3SApril Chin if (uid != ent->fts_statp->st_uid && uid != NOID || gid != ent->fts_statp->st_gid && gid != NOID)
434da2e3ebdSchin {
435da2e3ebdSchin if ((ent->fts_info & FTS_SL) && (flags & FTS_PHYSICAL) && (options & OPT_LCHOWN))
436da2e3ebdSchin {
437da2e3ebdSchin op = "lchown";
438da2e3ebdSchin chownf = lchown;
439da2e3ebdSchin }
440da2e3ebdSchin else
441da2e3ebdSchin {
442da2e3ebdSchin op = "chown";
443da2e3ebdSchin chownf = chown;
444da2e3ebdSchin }
445da2e3ebdSchin if (options & (OPT_SHOW|OPT_VERBOSE))
446da2e3ebdSchin {
447da2e3ebdSchin if (options & OPT_TEST)
448da2e3ebdSchin {
449da2e3ebdSchin ent->fts_statp->st_uid = 0;
450da2e3ebdSchin ent->fts_statp->st_gid = 0;
451da2e3ebdSchin }
4527c2fbfb3SApril Chin 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);
453da2e3ebdSchin }
454da2e3ebdSchin if (!(options & OPT_SHOW) && (*chownf)(ent->fts_accpath, uid, gid) && !(options & OPT_FORCE))
455da2e3ebdSchin error(ERROR_system(0), "%s: cannot change%s", ent->fts_accpath, s);
456da2e3ebdSchin }
457da2e3ebdSchin break;
458da2e3ebdSchin case FTS_DC:
459da2e3ebdSchin if (!(options & OPT_FORCE))
460da2e3ebdSchin error(ERROR_warn(0), "%s: directory causes cycle", ent->fts_accpath);
461da2e3ebdSchin break;
462da2e3ebdSchin case FTS_DNR:
463da2e3ebdSchin if (!(options & OPT_FORCE))
464da2e3ebdSchin error(ERROR_system(0), "%s: cannot read directory", ent->fts_accpath);
465da2e3ebdSchin goto anyway;
466da2e3ebdSchin case FTS_DNX:
467da2e3ebdSchin if (!(options & OPT_FORCE))
468da2e3ebdSchin error(ERROR_system(0), "%s: cannot search directory", ent->fts_accpath);
469da2e3ebdSchin goto anyway;
470da2e3ebdSchin case FTS_NS:
471da2e3ebdSchin if (!(options & OPT_FORCE))
472da2e3ebdSchin error(ERROR_system(0), "%s: not found", ent->fts_accpath);
473da2e3ebdSchin break;
474da2e3ebdSchin }
475da2e3ebdSchin fts_close(fts);
476da2e3ebdSchin if (map)
477da2e3ebdSchin dtclose(map);
478da2e3ebdSchin return error_info.errors != 0;
479da2e3ebdSchin }
480