xref: /onnv-gate/usr/src/lib/libcmd/common/chgrp.c (revision 12068:08a39a083754)
14887Schin /***********************************************************************
24887Schin *                                                                      *
34887Schin *               This software is part of the ast package               *
4*12068SRoger.Faulkner@Oracle.COM *          Copyright (c) 1992-2010 AT&T Intellectual Property          *
54887Schin *                      and is licensed under the                       *
64887Schin *                  Common Public License, Version 1.0                  *
78462SApril.Chin@Sun.COM *                    by AT&T Intellectual Property                     *
84887Schin *                                                                      *
94887Schin *                A copy of the License is available at                 *
104887Schin *            http://www.opensource.org/licenses/cpl1.0.txt             *
114887Schin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
124887Schin *                                                                      *
134887Schin *              Information and Software Systems Research               *
144887Schin *                            AT&T Research                             *
154887Schin *                           Florham Park NJ                            *
164887Schin *                                                                      *
174887Schin *                 Glenn Fowler <gsf@research.att.com>                  *
184887Schin *                  David Korn <dgk@research.att.com>                   *
194887Schin *                                                                      *
204887Schin ***********************************************************************/
214887Schin #pragma prototyped
224887Schin /*
234887Schin  * David Korn
244887Schin  * Glenn Fowler
254887Schin  * AT&T Research
264887Schin  *
274887Schin  * chgrp+chown
284887Schin  */
294887Schin 
304887Schin static const char usage_1[] =
3110898Sroland.mainz@nrubsig.org "[-?@(#)$Id: chgrp (AT&T Research) 2009-07-02 $\n]"
324887Schin USAGE_LICENSE
334887Schin ;
344887Schin 
354887Schin static const char usage_grp_1[] =
364887Schin "[+NAME?chgrp - change the group ownership of files]"
374887Schin "[+DESCRIPTION?\bchgrp\b changes the group ownership of each file"
384887Schin "	to \agroup\a, which can be either a group name or a numeric"
394887Schin "	group id. The user ownership of each file may also be changed to"
404887Schin "	\auser\a by prepending \auser\a\b:\b to the group name.]"
414887Schin ;
424887Schin 
434887Schin static const char usage_own_1[] =
444887Schin "[+NAME?chown - change the ownership of files]"
454887Schin "[+DESCRIPTION?\bchown\b changes the ownership of each file"
464887Schin "	to \auser\a, which can be either a user name or a numeric"
474887Schin "	user id. The group ownership of each file may also be changed to"
484887Schin "	\auser\a by appending \b:\b\agroup\a to the user name.]"
494887Schin ;
504887Schin 
514887Schin static const char usage_2[] =
528462SApril.Chin@Sun.COM "[b:before?Only change files with \bctime\b before (less than) the "
538462SApril.Chin@Sun.COM     "\bmtime\b of \afile\a.]:[file]"
544887Schin "[c:changes?Describe only files whose ownership actually changes.]"
554887Schin "[f:quiet|silent?Do not report files whose ownership fails to change.]"
568462SApril.Chin@Sun.COM "[l|h:symlink?Change the ownership of the symbolic links on systems that "
578462SApril.Chin@Sun.COM     "support this.]"
588462SApril.Chin@Sun.COM "[m:map?The first operand is interpreted as a file that contains a map "
598462SApril.Chin@Sun.COM     "of space separated \afrom_uid:from_gid to_uid:to_gid\a pairs. The "
608462SApril.Chin@Sun.COM     "\auid\a or \agid\a part of each pair may be omitted to mean any \auid\a "
618462SApril.Chin@Sun.COM     "or \agid\a. Ownership of files matching the \afrom\a part of any pair "
628462SApril.Chin@Sun.COM     "is changed to the corresponding \ato\a part of the pair. The matching "
638462SApril.Chin@Sun.COM     "for each file operand is in the order \auid\a:\agid\a, \auid\a:, "
648462SApril.Chin@Sun.COM     ":\agid\a. For a given file, once a \auid\a or \agid\a mapping is "
658462SApril.Chin@Sun.COM     "determined it is not overridden by any subsequent match. Unmatched "
668462SApril.Chin@Sun.COM     "files are silently ignored.]"
674887Schin "[n:show?Show actions but don't execute.]"
688462SApril.Chin@Sun.COM "[r:reference?Omit the explicit ownership operand and use the ownership "
698462SApril.Chin@Sun.COM     "of \afile\a instead.]:[file]"
708462SApril.Chin@Sun.COM "[u:unmapped?Print a diagnostic for each file for which either the "
718462SApril.Chin@Sun.COM     "\auid\a or \agid\a or both were not mapped.]"
724887Schin "[v:verbose?Describe changed permissions of all files.]"
738462SApril.Chin@Sun.COM "[H:metaphysical?Follow symbolic links for command arguments; otherwise "
748462SApril.Chin@Sun.COM     "don't follow symbolic links when traversing directories.]"
754887Schin "[L:logical|follow?Follow symbolic links when traversing directories.]"
768462SApril.Chin@Sun.COM "[P:physical|nofollow?Don't follow symbolic links when traversing "
778462SApril.Chin@Sun.COM     "directories.]"
788462SApril.Chin@Sun.COM "[R:recursive?Recursively change ownership of directories and their "
798462SApril.Chin@Sun.COM     "contents.]"
804887Schin "[X:test?Canonicalize output for testing.]"
814887Schin 
824887Schin "\n"
834887Schin "\n"
844887Schin ;
854887Schin 
864887Schin static const char usage_3[] =
874887Schin " file ...\n"
884887Schin "\n"
894887Schin "[+EXIT STATUS?]{"
904887Schin 	"[+0?All files changed successfully.]"
914887Schin 	"[+>0?Unable to change ownership of one or more files.]"
924887Schin "}"
934887Schin "[+SEE ALSO?\bchmod\b(1), \btw\b(1), \bgetconf\b(1), \bls\b(1)]"
944887Schin ;
954887Schin 
964887Schin #if defined(__STDPP__directive) && defined(__STDPP__hide)
974887Schin __STDPP__directive pragma pp:hide lchown
984887Schin #else
994887Schin #define lchown		______lchown
1004887Schin #endif
1014887Schin 
1024887Schin #include <cmd.h>
1034887Schin #include <cdt.h>
1044887Schin #include <ls.h>
1054887Schin #include <ctype.h>
106*12068SRoger.Faulkner@Oracle.COM #include <fts_fix.h>
1074887Schin 
1084887Schin #include "FEATURE/symlink"
1094887Schin 
1104887Schin #if defined(__STDPP__directive) && defined(__STDPP__hide)
1114887Schin __STDPP__directive pragma pp:nohide lchown
1124887Schin #else
1134887Schin #undef	lchown
1144887Schin #endif
1154887Schin 
1168462SApril.Chin@Sun.COM typedef struct Key_s			/* uid/gid key			*/
1178462SApril.Chin@Sun.COM {
1188462SApril.Chin@Sun.COM 	int		uid;		/* uid				*/
1198462SApril.Chin@Sun.COM 	int		gid;		/* gid				*/
1208462SApril.Chin@Sun.COM } Key_t;
1218462SApril.Chin@Sun.COM 
1228462SApril.Chin@Sun.COM typedef struct Map_s			/* uid/gid map			*/
1234887Schin {
1244887Schin 	Dtlink_t	link;		/* dictionary link		*/
1258462SApril.Chin@Sun.COM 	Key_t		key;		/* key				*/
1268462SApril.Chin@Sun.COM 	Key_t		to;		/* map to these			*/
1274887Schin } Map_t;
1284887Schin 
1294887Schin #define NOID		(-1)
1304887Schin 
1314887Schin #define OPT_CHOWN	(1<<0)		/* chown			*/
1324887Schin #define OPT_FORCE	(1<<1)		/* ignore errors		*/
1334887Schin #define OPT_GID		(1<<2)		/* have gid			*/
1344887Schin #define OPT_LCHOWN	(1<<3)		/* lchown			*/
1354887Schin #define OPT_SHOW	(1<<4)		/* show but don't do		*/
1364887Schin #define OPT_TEST	(1<<5)		/* canonicalize output		*/
1374887Schin #define OPT_UID		(1<<6)		/* have uid			*/
1388462SApril.Chin@Sun.COM #define OPT_UNMAPPED	(1<<7)		/* unmapped file diagnostic	*/
1398462SApril.Chin@Sun.COM #define OPT_VERBOSE	(1<<8)		/* have uid			*/
1404887Schin 
1414887Schin extern int	lchown(const char*, uid_t, gid_t);
1424887Schin 
1434887Schin #if !_lib_lchown
1444887Schin 
1454887Schin #ifndef ENOSYS
1464887Schin #define ENOSYS	EINVAL
1474887Schin #endif
1484887Schin 
1494887Schin int
lchown(const char * path,uid_t uid,gid_t gid)1504887Schin lchown(const char* path, uid_t uid, gid_t gid)
1514887Schin {
1524887Schin 	return ENOSYS;
1534887Schin }
1544887Schin 
1554887Schin #endif /* _lib_chown */
1564887Schin 
1574887Schin /*
1584887Schin  * parse uid and gid from s
1594887Schin  */
1604887Schin 
1614887Schin static void
getids(register char * s,char ** e,Key_t * key,int options)1628462SApril.Chin@Sun.COM getids(register char* s, char** e, Key_t* key, int options)
1634887Schin {
1644887Schin 	register char*	t;
1654887Schin 	register int	n;
1664887Schin 	char*		z;
1674887Schin 	char		buf[64];
1684887Schin 
1698462SApril.Chin@Sun.COM 	key->uid = key->gid = NOID;
1704887Schin 	while (isspace(*s))
1714887Schin 		s++;
1724887Schin 	for (t = s; (n = *t) && n != ':' && n != '.' && !isspace(n); t++);
1734887Schin 	if (n)
1744887Schin 	{
1754887Schin 		options |= OPT_CHOWN;
1764887Schin 		if ((n = t++ - s) >= sizeof(buf))
1774887Schin 			n = sizeof(buf) - 1;
1784887Schin 		*((s = (char*)memcpy(buf, s, n)) + n) = 0;
1794887Schin 	}
1804887Schin 	if (options & OPT_CHOWN)
1814887Schin 	{
1824887Schin 		if (*s)
1834887Schin 		{
1844887Schin 			if ((n = struid(s)) == NOID)
1854887Schin 			{
1864887Schin 				n = (int)strtol(s, &z, 0);
1874887Schin 				if (*z)
1884887Schin 					error(ERROR_exit(1), "%s: unknown user", s);
1894887Schin 			}
1908462SApril.Chin@Sun.COM 			key->uid = n;
1914887Schin 		}
1924887Schin 		for (s = t; (n = *t) && !isspace(n); t++);
1934887Schin 		if (n)
1944887Schin 		{
1954887Schin 			if ((n = t++ - s) >= sizeof(buf))
1964887Schin 				n = sizeof(buf) - 1;
1974887Schin 			*((s = (char*)memcpy(buf, s, n)) + n) = 0;
1984887Schin 		}
1994887Schin 	}
2004887Schin 	if (*s)
2014887Schin 	{
2024887Schin 		if ((n = strgid(s)) == NOID)
2034887Schin 		{
2044887Schin 			n = (int)strtol(s, &z, 0);
2054887Schin 			if (*z)
2064887Schin 				error(ERROR_exit(1), "%s: unknown group", s);
2074887Schin 		}
2088462SApril.Chin@Sun.COM 		key->gid = n;
2094887Schin 	}
2104887Schin 	if (e)
2114887Schin 		*e = t;
2124887Schin }
2134887Schin 
2144887Schin int
b_chgrp(int argc,char ** argv,void * context)2154887Schin b_chgrp(int argc, char** argv, void* context)
2164887Schin {
2174887Schin 	register int	options = 0;
2184887Schin 	register char*	s;
2194887Schin 	register Map_t*	m;
2204887Schin 	register FTS*	fts;
2214887Schin 	register FTSENT*ent;
2228462SApril.Chin@Sun.COM 	register int	i;
2234887Schin 	Dt_t*		map = 0;
22410898Sroland.mainz@nrubsig.org 	int		logical = 1;
2254887Schin 	int		flags;
2264887Schin 	int		uid;
2274887Schin 	int		gid;
2284887Schin 	char*		op;
2294887Schin 	char*		usage;
2308462SApril.Chin@Sun.COM 	char*		t;
2314887Schin 	Sfio_t*		sp;
2328462SApril.Chin@Sun.COM 	unsigned long	before;
2334887Schin 	Dtdisc_t	mapdisc;
2348462SApril.Chin@Sun.COM 	Key_t		keys[3];
2358462SApril.Chin@Sun.COM 	Key_t		key;
2364887Schin 	struct stat	st;
2374887Schin 	int		(*chownf)(const char*, uid_t, gid_t);
2384887Schin 
2394887Schin 	cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
2404887Schin 	flags = fts_flags() | FTS_TOP | FTS_NOPOSTORDER | FTS_NOSEEDOTDIR;
2418462SApril.Chin@Sun.COM 	before = ~0;
2424887Schin 	if (!(sp = sfstropen()))
2434887Schin 		error(ERROR_SYSTEM|3, "out of space");
2444887Schin 	sfputr(sp, usage_1, -1);
2454887Schin 	if (error_info.id[2] == 'g')
2464887Schin 		sfputr(sp, usage_grp_1, -1);
2474887Schin 	else
2484887Schin 	{
2494887Schin 		sfputr(sp, usage_own_1, -1);
2504887Schin 		options |= OPT_CHOWN;
2514887Schin 	}
2524887Schin 	sfputr(sp, usage_2, -1);
2534887Schin 	if (options & OPT_CHOWN)
2544887Schin 		sfputr(sp, ERROR_translate(0, 0, 0, "[owner[:group]]"), -1);
2554887Schin 	else
2564887Schin 		sfputr(sp, ERROR_translate(0, 0, 0, "[[owner:]group]"), -1);
2574887Schin 	sfputr(sp, usage_3, -1);
2584887Schin 	if (!(usage = sfstruse(sp)))
2594887Schin 		error(ERROR_SYSTEM|3, "out of space");
2604887Schin 	for (;;)
2614887Schin 	{
2624887Schin 		switch (optget(argv, usage))
2634887Schin 		{
2648462SApril.Chin@Sun.COM 		case 'b':
2658462SApril.Chin@Sun.COM 			if (stat(opt_info.arg, &st))
2668462SApril.Chin@Sun.COM 				error(ERROR_exit(1), "%s: cannot stat", opt_info.arg);
2678462SApril.Chin@Sun.COM 			before = st.st_mtime;
2688462SApril.Chin@Sun.COM 			continue;
2694887Schin 		case 'c':
2704887Schin 		case 'v':
2714887Schin 			options |= OPT_VERBOSE;
2724887Schin 			continue;
2734887Schin 		case 'f':
2744887Schin 			options |= OPT_FORCE;
2754887Schin 			continue;
2764887Schin 		case 'l':
2774887Schin 			options |= OPT_LCHOWN;
2784887Schin 			continue;
2794887Schin 		case 'm':
2804887Schin 			memset(&mapdisc, 0, sizeof(mapdisc));
2818462SApril.Chin@Sun.COM 			mapdisc.key = offsetof(Map_t, key);
2828462SApril.Chin@Sun.COM 			mapdisc.size = sizeof(Key_t);
2834887Schin 			if (!(map = dtopen(&mapdisc, Dthash)))
2844887Schin 				error(ERROR_exit(1), "out of space [id map]");
2854887Schin 			continue;
2864887Schin 		case 'n':
2874887Schin 			options |= OPT_SHOW;
2884887Schin 			continue;
2894887Schin 		case 'r':
2904887Schin 			if (stat(opt_info.arg, &st))
2914887Schin 				error(ERROR_exit(1), "%s: cannot stat", opt_info.arg);
2924887Schin 			uid = st.st_uid;
2934887Schin 			gid = st.st_gid;
2944887Schin 			options |= OPT_UID|OPT_GID;
2954887Schin 			continue;
2968462SApril.Chin@Sun.COM 		case 'u':
2978462SApril.Chin@Sun.COM 			options |= OPT_UNMAPPED;
2988462SApril.Chin@Sun.COM 			continue;
2994887Schin 		case 'H':
3004887Schin 			flags |= FTS_META|FTS_PHYSICAL;
30110898Sroland.mainz@nrubsig.org 			logical = 0;
3024887Schin 			continue;
3034887Schin 		case 'L':
3044887Schin 			flags &= ~(FTS_META|FTS_PHYSICAL);
30510898Sroland.mainz@nrubsig.org 			logical = 0;
3064887Schin 			continue;
3074887Schin 		case 'P':
3084887Schin 			flags &= ~FTS_META;
3094887Schin 			flags |= FTS_PHYSICAL;
31010898Sroland.mainz@nrubsig.org 			logical = 0;
3114887Schin 			continue;
3124887Schin 		case 'R':
3134887Schin 			flags &= ~FTS_TOP;
31410898Sroland.mainz@nrubsig.org 			logical = 0;
3154887Schin 			continue;
3164887Schin 		case 'X':
3174887Schin 			options |= OPT_TEST;
3184887Schin 			continue;
3194887Schin 		case ':':
3204887Schin 			error(2, "%s", opt_info.arg);
3214887Schin 			continue;
3224887Schin 		case '?':
3234887Schin 			error(ERROR_usage(2), "%s", opt_info.arg);
3244887Schin 			break;
3254887Schin 		}
3264887Schin 		break;
3274887Schin 	}
3284887Schin 	argv += opt_info.index;
3294887Schin 	argc -= opt_info.index;
3304887Schin 	if (error_info.errors || argc < 2)
3314887Schin 		error(ERROR_usage(2), "%s", optusage(NiL));
3324887Schin 	s = *argv;
33310898Sroland.mainz@nrubsig.org 	if (logical)
33410898Sroland.mainz@nrubsig.org 		flags &= ~(FTS_META|FTS_PHYSICAL);
3354887Schin 	if (map)
3364887Schin 	{
3374887Schin 		if (streq(s, "-"))
3384887Schin 			sp = sfstdin;
3394887Schin 		else if (!(sp = sfopen(NiL, s, "r")))
3404887Schin 			error(ERROR_exit(1), "%s: cannot read", s);
3414887Schin 		while (s = sfgetr(sp, '\n', 1))
3424887Schin 		{
3438462SApril.Chin@Sun.COM 			getids(s, &t, &key, options);
3448462SApril.Chin@Sun.COM 			if (!(m = (Map_t*)dtmatch(map, &key)))
3454887Schin 			{
3468462SApril.Chin@Sun.COM 				if (!(m = (Map_t*)stakalloc(sizeof(Map_t))))
3474887Schin 					error(ERROR_exit(1), "out of space [id dictionary]");
3488462SApril.Chin@Sun.COM 				m->key = key;
3498462SApril.Chin@Sun.COM 				m->to.uid = m->to.gid = NOID;
3508462SApril.Chin@Sun.COM 				dtinsert(map, m);
3514887Schin 			}
3528462SApril.Chin@Sun.COM 			getids(t, NiL, &m->to, options);
3534887Schin 		}
3544887Schin 		if (sp != sfstdin)
3554887Schin 			sfclose(sp);
3568462SApril.Chin@Sun.COM 		keys[1].gid = keys[2].uid = NOID;
3574887Schin 	}
3584887Schin 	else if (!(options & (OPT_UID|OPT_GID)))
3594887Schin 	{
3608462SApril.Chin@Sun.COM 		getids(s, NiL, &key, options);
3618462SApril.Chin@Sun.COM 		if ((uid = key.uid) != NOID)
3624887Schin 			options |= OPT_UID;
3638462SApril.Chin@Sun.COM 		if ((gid = key.gid) != NOID)
3644887Schin 			options |= OPT_GID;
3654887Schin 	}
3664887Schin 	switch (options & (OPT_UID|OPT_GID))
3674887Schin 	{
3684887Schin 	case OPT_UID:
3694887Schin 		s = ERROR_translate(0, 0, 0, " owner");
3704887Schin 		break;
3714887Schin 	case OPT_GID:
3724887Schin 		s = ERROR_translate(0, 0, 0, " group");
3734887Schin 		break;
3744887Schin 	case OPT_UID|OPT_GID:
3754887Schin 		s = ERROR_translate(0, 0, 0, " owner and group");
3764887Schin 		break;
3774887Schin 	default:
3784887Schin 		s = "";
3794887Schin 		break;
3804887Schin 	}
3814887Schin 	if (!(fts = fts_open(argv + 1, flags, NiL)))
3824887Schin 		error(ERROR_system(1), "%s: not found", argv[1]);
3838462SApril.Chin@Sun.COM 	while (!sh_checksig(context) && (ent = fts_read(fts)))
3844887Schin 		switch (ent->fts_info)
3854887Schin 		{
3864887Schin 		case FTS_F:
3874887Schin 		case FTS_D:
3884887Schin 		case FTS_SL:
3894887Schin 		case FTS_SLNONE:
3904887Schin 		anyway:
3918462SApril.Chin@Sun.COM 			if ((unsigned long)ent->fts_statp->st_ctime >= before)
3928462SApril.Chin@Sun.COM 				break;
3934887Schin 			if (map)
3944887Schin 			{
3954887Schin 				options &= ~(OPT_UID|OPT_GID);
3968462SApril.Chin@Sun.COM 				uid = gid = NOID;
3978462SApril.Chin@Sun.COM 				keys[0].uid = keys[1].uid = ent->fts_statp->st_uid;
3988462SApril.Chin@Sun.COM 				keys[0].gid = keys[2].gid = ent->fts_statp->st_gid;
3998462SApril.Chin@Sun.COM 				i = 0;
4008462SApril.Chin@Sun.COM 				do
4014887Schin 				{
4028462SApril.Chin@Sun.COM 					if (m = (Map_t*)dtmatch(map, &keys[i]))
4038462SApril.Chin@Sun.COM 					{
4048462SApril.Chin@Sun.COM 						if (uid == NOID && m->to.uid != NOID)
4058462SApril.Chin@Sun.COM 						{
4068462SApril.Chin@Sun.COM 							uid = m->to.uid;
4078462SApril.Chin@Sun.COM 							options |= OPT_UID;
4088462SApril.Chin@Sun.COM 						}
4098462SApril.Chin@Sun.COM 						if (gid == NOID && m->to.gid != NOID)
4108462SApril.Chin@Sun.COM 						{
4118462SApril.Chin@Sun.COM 							gid = m->to.gid;
4128462SApril.Chin@Sun.COM 							options |= OPT_GID;
4138462SApril.Chin@Sun.COM 						}
4148462SApril.Chin@Sun.COM 					}
4158462SApril.Chin@Sun.COM 				} while (++i < elementsof(keys) && (uid == NOID || gid == NOID));
4164887Schin 			}
4174887Schin 			else
4184887Schin 			{
4194887Schin 				if (!(options & OPT_UID))
4204887Schin 					uid = ent->fts_statp->st_uid;
4214887Schin 				if (!(options & OPT_GID))
4224887Schin 					gid = ent->fts_statp->st_gid;
4234887Schin 			}
4248462SApril.Chin@Sun.COM 			if ((options & OPT_UNMAPPED) && (uid == NOID || gid == NOID))
4258462SApril.Chin@Sun.COM 			{
4268462SApril.Chin@Sun.COM 				if (uid == NOID && gid == NOID)
4278462SApril.Chin@Sun.COM 					error(ERROR_warn(0), "%s: uid and gid not mapped", ent->fts_path);
4288462SApril.Chin@Sun.COM 				else if (uid == NOID)
4298462SApril.Chin@Sun.COM 					error(ERROR_warn(0), "%s: uid not mapped", ent->fts_path);
4308462SApril.Chin@Sun.COM 				else
4318462SApril.Chin@Sun.COM 					error(ERROR_warn(0), "%s: gid not mapped", ent->fts_path);
4328462SApril.Chin@Sun.COM 			}
4338462SApril.Chin@Sun.COM 			if (uid != ent->fts_statp->st_uid && uid != NOID || gid != ent->fts_statp->st_gid && gid != NOID)
4344887Schin 			{
4354887Schin 				if ((ent->fts_info & FTS_SL) && (flags & FTS_PHYSICAL) && (options & OPT_LCHOWN))
4364887Schin 				{
4374887Schin 					op = "lchown";
4384887Schin 					chownf = lchown;
4394887Schin 				}
4404887Schin 				else
4414887Schin 				{
4424887Schin 					op = "chown";
4434887Schin 					chownf = chown;
4444887Schin 				}
4454887Schin 				if (options & (OPT_SHOW|OPT_VERBOSE))
4464887Schin 				{
4474887Schin 					if (options & OPT_TEST)
4484887Schin 					{
4494887Schin 						ent->fts_statp->st_uid = 0;
4504887Schin 						ent->fts_statp->st_gid = 0;
4514887Schin 					}
4528462SApril.Chin@Sun.COM 					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);
4534887Schin 				}
4544887Schin 				if (!(options & OPT_SHOW) && (*chownf)(ent->fts_accpath, uid, gid) && !(options & OPT_FORCE))
4554887Schin 					error(ERROR_system(0), "%s: cannot change%s", ent->fts_accpath, s);
4564887Schin 			}
4574887Schin 			break;
4584887Schin 		case FTS_DC:
4594887Schin 			if (!(options & OPT_FORCE))
4604887Schin 				error(ERROR_warn(0), "%s: directory causes cycle", ent->fts_accpath);
4614887Schin 			break;
4624887Schin 		case FTS_DNR:
4634887Schin 			if (!(options & OPT_FORCE))
4644887Schin 				error(ERROR_system(0), "%s: cannot read directory", ent->fts_accpath);
4654887Schin 			goto anyway;
4664887Schin 		case FTS_DNX:
4674887Schin 			if (!(options & OPT_FORCE))
4684887Schin 				error(ERROR_system(0), "%s: cannot search directory", ent->fts_accpath);
4694887Schin 			goto anyway;
4704887Schin 		case FTS_NS:
4714887Schin 			if (!(options & OPT_FORCE))
4724887Schin 				error(ERROR_system(0), "%s: not found", ent->fts_accpath);
4734887Schin 			break;
4744887Schin 		}
4754887Schin 	fts_close(fts);
4764887Schin 	if (map)
4774887Schin 		dtclose(map);
4784887Schin 	return error_info.errors != 0;
4794887Schin }
480