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