mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-03-09 15:50:02 +00:00
This was: /* * -lcmd specific workaround to handle * fts_namelen * fts_pathlen * fts_level * changing from [unsigned] short bit to [s]size_t * * ksh (or any other main application) that pulls in -lcmd * at runtime may result in old -last running with new -lcmd * which is not a good situation (tm) * * probably safe to drop after 20150101 */ According to the version check in fts_fix.c, this change occurred in the libast API version 2010-01-02, which is also the API version of the bundled libast (see src/lib/libast/misc/state.c). src/lib/libcmd/fts_fix.{c,h}: - Removed. src/lib/libcmd/{chgrp,chmod,cksum,cp,rm}.c: - Change uses of fts_fix.h to fts.h from libast. src/lib/libcmd/Mamfile: - Update accordingly.
531 lines
14 KiB
C
531 lines
14 KiB
C
/***********************************************************************
|
|
* *
|
|
* This software is part of the ast package *
|
|
* Copyright (c) 1992-2012 AT&T Intellectual Property *
|
|
* Copyright (c) 2020-2021 Contributors to ksh 93u+m *
|
|
* and is licensed under the *
|
|
* Eclipse Public License, Version 1.0 *
|
|
* by AT&T Intellectual Property *
|
|
* *
|
|
* A copy of the License is available at *
|
|
* http://www.eclipse.org/org/documents/epl-v10.html *
|
|
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
|
|
* *
|
|
* Information and Software Systems Research *
|
|
* AT&T Research *
|
|
* Florham Park NJ *
|
|
* *
|
|
* Glenn Fowler <gsf@research.att.com> *
|
|
* David Korn <dgk@research.att.com> *
|
|
* *
|
|
***********************************************************************/
|
|
#pragma prototyped
|
|
/*
|
|
* David Korn
|
|
* Glenn Fowler
|
|
* AT&T Research
|
|
*
|
|
* chgrp+chown
|
|
*/
|
|
|
|
static const char usage_1[] =
|
|
"[-?@(#)$Id: chgrp (AT&T Research) 2012-04-20 $\n]"
|
|
"[--catalog?" ERROR_CATALOG "]"
|
|
;
|
|
|
|
static const char usage_grp_1[] =
|
|
"[+NAME?chgrp - change the group ownership of files]"
|
|
"[+DESCRIPTION?\bchgrp\b changes the group ownership of each file"
|
|
" to \agroup\a, which can be either a group name or a numeric"
|
|
" group id. The user ownership of each file may also be changed to"
|
|
" \auser\a by prepending \auser\a\b:\b to the group name.]"
|
|
;
|
|
|
|
static const char usage_own_1[] =
|
|
"[+NAME?chown - change the ownership of files]"
|
|
"[+DESCRIPTION?\bchown\b changes the ownership of each file"
|
|
" to \auser\a, which can be either a user name or a numeric"
|
|
" user id. The group ownership of each file may also be changed to"
|
|
" \auser\a by appending \b:\b\agroup\a to the user name.]"
|
|
;
|
|
|
|
static const char usage_2[] =
|
|
"[b:before?Only change files with \bctime\b before (less than) the "
|
|
"\bmtime\b of \afile\a.]:[file]"
|
|
"[c:changes?Describe only files whose ownership actually changes.]"
|
|
"[f:quiet|silent?Do not report files whose ownership fails to change.]"
|
|
"[h|l:symlink?Change the ownership of symbolic links on systems that "
|
|
"support \blchown\b(2). Implies \b--physical\b.]"
|
|
"[m:map?The first operand is interpreted as a file that contains a map "
|
|
"of space separated \afrom_uid:from_gid to_uid:to_gid\a pairs. The "
|
|
"\auid\a or \agid\a part of each pair may be omitted to mean any \auid\a "
|
|
"or \agid\a. Ownership of files matching the \afrom\a part of any pair "
|
|
"is changed to the corresponding \ato\a part of the pair. The matching "
|
|
"for each file operand is in the order \auid\a:\agid\a, \auid\a:, "
|
|
":\agid\a. For a given file, once a \auid\a or \agid\a mapping is "
|
|
"determined it is not overridden by any subsequent match. Unmatched "
|
|
"files are silently ignored.]"
|
|
"[n:show?Show actions but don't execute.]"
|
|
"[N:numeric?By default numeric user and group id operands are first "
|
|
"interpreted as names; if no name exists then they are interpreted as "
|
|
"explicit numeric ids. \b--numeric\b interprets numeric id operands as "
|
|
"numeric ids.]"
|
|
"[r:reference?Omit the explicit ownership operand and use the ownership "
|
|
"of \afile\a instead.]:[file]"
|
|
"[u:unmapped?Print a diagnostic for each file for which either the "
|
|
"\auid\a or \agid\a or both were not mapped.]"
|
|
"[v:verbose?Describe changed permissions of all files.]"
|
|
"[H:metaphysical?Follow symbolic links for command arguments; otherwise "
|
|
"don't follow symbolic links when traversing directories.]"
|
|
"[L:logical|follow?Follow symbolic links when traversing directories.]"
|
|
"[P:physical|nofollow?Don't follow symbolic links when traversing "
|
|
"directories.]"
|
|
"[R:recursive?Recursively change ownership of directories and their "
|
|
"contents.]"
|
|
"[X:test?Canonicalize output for testing.]"
|
|
|
|
"\n"
|
|
"\n"
|
|
;
|
|
|
|
static const char usage_3[] =
|
|
" file ...\n"
|
|
"\n"
|
|
"[+EXIT STATUS?]{"
|
|
"[+0?All files changed successfully.]"
|
|
"[+>0?Unable to change ownership of one or more files.]"
|
|
"}"
|
|
"[+SEE ALSO?\bchmod\b(1), \bchown\b(2), \btw\b(1), \bgetconf\b(1), \bls\b(1)]"
|
|
;
|
|
|
|
#if defined(__STDPP__directive) && defined(__STDPP__hide)
|
|
__STDPP__directive pragma pp:hide lchown
|
|
#else
|
|
#define lchown ______lchown
|
|
#endif
|
|
|
|
#include <cmd.h>
|
|
#include <cdt.h>
|
|
#include <ls.h>
|
|
#include <ctype.h>
|
|
#include <fts.h>
|
|
|
|
#include "FEATURE/symlink"
|
|
|
|
#if defined(__STDPP__directive) && defined(__STDPP__hide)
|
|
__STDPP__directive pragma pp:nohide lchown
|
|
#else
|
|
#undef lchown
|
|
#endif
|
|
|
|
typedef struct Key_s /* uid/gid key */
|
|
{
|
|
int uid; /* uid */
|
|
int gid; /* gid */
|
|
} Key_t;
|
|
|
|
typedef struct Map_s /* uid/gid map */
|
|
{
|
|
Dtlink_t link; /* dictionary link */
|
|
Key_t key; /* key */
|
|
Key_t to; /* map to these */
|
|
} Map_t;
|
|
|
|
#define NOID (-1)
|
|
|
|
#define OPT_CHOWN 0x0001 /* chown */
|
|
#define OPT_FORCE 0x0002 /* ignore errors */
|
|
#define OPT_GID 0x0004 /* have gid */
|
|
#define OPT_LCHOWN 0x0008 /* lchown */
|
|
#define OPT_NUMERIC 0x0010 /* favor numeric ids */
|
|
#define OPT_SHOW 0x0020 /* show but don't do */
|
|
#define OPT_TEST 0x0040 /* canonicalize output */
|
|
#define OPT_UID 0x0080 /* have uid */
|
|
#define OPT_UNMAPPED 0x0100 /* unmapped file diagnostic */
|
|
#define OPT_VERBOSE 0x0200 /* have uid */
|
|
|
|
extern int lchown(const char*, uid_t, gid_t);
|
|
|
|
/*
|
|
* parse uid and gid from s
|
|
*/
|
|
|
|
static void
|
|
getids(register char* s, char** e, Key_t* key, int options)
|
|
{
|
|
register char* t;
|
|
register int n;
|
|
register int m;
|
|
char* z;
|
|
char buf[64];
|
|
|
|
key->uid = key->gid = NOID;
|
|
while (isspace(*s))
|
|
s++;
|
|
for (t = s; (n = *t) && n != ':' && n != '.' && !isspace(n); t++);
|
|
if (n)
|
|
{
|
|
options |= OPT_CHOWN;
|
|
if ((n = t++ - s) >= sizeof(buf))
|
|
n = sizeof(buf) - 1;
|
|
*((s = (char*)memcpy(buf, s, n)) + n) = 0;
|
|
}
|
|
if (options & OPT_CHOWN)
|
|
{
|
|
if (*s)
|
|
{
|
|
n = (int)strtol(s, &z, 0);
|
|
if (*z || !(options & OPT_NUMERIC))
|
|
{
|
|
if ((m = struid(s)) != NOID)
|
|
n = m;
|
|
else if (*z)
|
|
{
|
|
error(ERROR_exit(1), "%s: unknown user", s);
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
key->uid = n;
|
|
}
|
|
for (s = t; (n = *t) && !isspace(n); t++);
|
|
if (n)
|
|
{
|
|
if ((n = t++ - s) >= sizeof(buf))
|
|
n = sizeof(buf) - 1;
|
|
*((s = (char*)memcpy(buf, s, n)) + n) = 0;
|
|
}
|
|
}
|
|
if (*s)
|
|
{
|
|
n = (int)strtol(s, &z, 0);
|
|
if (*z || !(options & OPT_NUMERIC))
|
|
{
|
|
if ((m = strgid(s)) != NOID)
|
|
n = m;
|
|
else if (*z)
|
|
{
|
|
error(ERROR_exit(1), "%s: unknown group", s);
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
key->gid = n;
|
|
}
|
|
if (e)
|
|
*e = t;
|
|
}
|
|
|
|
/*
|
|
* NOTE: we only use the native lchown() on symlinks just in case
|
|
* the implementation is a feckless stub
|
|
*/
|
|
|
|
int
|
|
b_chgrp(int argc, char** argv, Shbltin_t* context)
|
|
{
|
|
register int options = 0;
|
|
register char* s;
|
|
register Map_t* m;
|
|
register FTS* fts;
|
|
register FTSENT*ent;
|
|
register int i;
|
|
Dt_t* map = 0;
|
|
int logical = 1;
|
|
int flags;
|
|
int uid;
|
|
int gid;
|
|
char* op;
|
|
char* usage;
|
|
char* t;
|
|
Sfio_t* sp;
|
|
unsigned long before;
|
|
Dtdisc_t mapdisc;
|
|
Key_t keys[3];
|
|
Key_t key;
|
|
struct stat st;
|
|
int (*chownf)(const char*, uid_t, gid_t);
|
|
|
|
cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
|
|
flags = fts_flags() | FTS_META | FTS_TOP | FTS_NOPOSTORDER | FTS_NOSEEDOTDIR;
|
|
before = ~0;
|
|
if (!(sp = sfstropen()))
|
|
{
|
|
error(ERROR_SYSTEM|3, "out of space");
|
|
UNREACHABLE();
|
|
}
|
|
sfputr(sp, usage_1, -1);
|
|
if (error_info.id[2] == 'g')
|
|
sfputr(sp, usage_grp_1, -1);
|
|
else
|
|
{
|
|
sfputr(sp, usage_own_1, -1);
|
|
options |= OPT_CHOWN;
|
|
}
|
|
sfputr(sp, usage_2, -1);
|
|
if (options & OPT_CHOWN)
|
|
sfputr(sp, ERROR_translate(0, 0, 0, "[owner[:group]]"), -1);
|
|
else
|
|
sfputr(sp, ERROR_translate(0, 0, 0, "[[owner:]group]"), -1);
|
|
sfputr(sp, usage_3, -1);
|
|
if (!(usage = sfstruse(sp)))
|
|
{
|
|
error(ERROR_SYSTEM|3, "out of space");
|
|
UNREACHABLE();
|
|
}
|
|
for (;;)
|
|
{
|
|
switch (optget(argv, usage))
|
|
{
|
|
case 'b':
|
|
if (stat(opt_info.arg, &st))
|
|
{
|
|
error(ERROR_exit(1), "%s: cannot stat", opt_info.arg);
|
|
UNREACHABLE();
|
|
}
|
|
before = st.st_mtime;
|
|
continue;
|
|
case 'c':
|
|
case 'v':
|
|
options |= OPT_VERBOSE;
|
|
continue;
|
|
case 'f':
|
|
options |= OPT_FORCE;
|
|
continue;
|
|
case 'h':
|
|
options |= OPT_LCHOWN;
|
|
continue;
|
|
case 'm':
|
|
memset(&mapdisc, 0, sizeof(mapdisc));
|
|
mapdisc.key = offsetof(Map_t, key);
|
|
mapdisc.size = sizeof(Key_t);
|
|
if (!(map = dtopen(&mapdisc, Dtset)))
|
|
{
|
|
error(ERROR_exit(1), "out of memory [id map]");
|
|
UNREACHABLE();
|
|
}
|
|
continue;
|
|
case 'n':
|
|
options |= OPT_SHOW;
|
|
continue;
|
|
case 'N':
|
|
options |= OPT_NUMERIC;
|
|
continue;
|
|
case 'r':
|
|
if (stat(opt_info.arg, &st))
|
|
{
|
|
error(ERROR_exit(1), "%s: cannot stat", opt_info.arg);
|
|
UNREACHABLE();
|
|
}
|
|
uid = st.st_uid;
|
|
gid = st.st_gid;
|
|
options |= OPT_UID|OPT_GID;
|
|
continue;
|
|
case 'u':
|
|
options |= OPT_UNMAPPED;
|
|
continue;
|
|
case 'H':
|
|
flags |= FTS_META|FTS_PHYSICAL;
|
|
logical = 0;
|
|
continue;
|
|
case 'L':
|
|
flags &= ~(FTS_META|FTS_PHYSICAL);
|
|
logical = 0;
|
|
continue;
|
|
case 'P':
|
|
flags &= ~FTS_META;
|
|
flags |= FTS_PHYSICAL;
|
|
logical = 0;
|
|
continue;
|
|
case 'R':
|
|
flags &= ~FTS_TOP;
|
|
logical = 0;
|
|
continue;
|
|
case 'X':
|
|
options |= OPT_TEST;
|
|
continue;
|
|
case ':':
|
|
error(2, "%s", opt_info.arg);
|
|
continue;
|
|
case '?':
|
|
error(ERROR_usage(2), "%s", opt_info.arg);
|
|
UNREACHABLE();
|
|
}
|
|
break;
|
|
}
|
|
argv += opt_info.index;
|
|
argc -= opt_info.index;
|
|
if (error_info.errors || argc < 2)
|
|
{
|
|
error(ERROR_usage(2), "%s", optusage(NiL));
|
|
UNREACHABLE();
|
|
}
|
|
s = *argv;
|
|
if (options & OPT_LCHOWN)
|
|
{
|
|
flags &= ~FTS_META;
|
|
flags |= FTS_PHYSICAL;
|
|
logical = 0;
|
|
}
|
|
if (logical)
|
|
flags &= ~(FTS_META|FTS_PHYSICAL);
|
|
if (map)
|
|
{
|
|
if (streq(s, "-"))
|
|
sp = sfstdin;
|
|
else if (!(sp = sfopen(NiL, s, "r")))
|
|
{
|
|
error(ERROR_exit(1), "%s: cannot read", s);
|
|
UNREACHABLE();
|
|
}
|
|
while (s = sfgetr(sp, '\n', 1))
|
|
{
|
|
getids(s, &t, &key, options);
|
|
if (!(m = (Map_t*)dtmatch(map, &key)))
|
|
{
|
|
if (!(m = (Map_t*)stakalloc(sizeof(Map_t))))
|
|
{
|
|
error(ERROR_exit(1), "out of memory [id dictionary]");
|
|
UNREACHABLE();
|
|
}
|
|
m->key = key;
|
|
m->to.uid = m->to.gid = NOID;
|
|
dtinsert(map, m);
|
|
}
|
|
getids(t, NiL, &m->to, options);
|
|
}
|
|
if (sp != sfstdin)
|
|
sfclose(sp);
|
|
keys[1].gid = keys[2].uid = NOID;
|
|
}
|
|
else if (!(options & (OPT_UID|OPT_GID)))
|
|
{
|
|
getids(s, NiL, &key, options);
|
|
if ((uid = key.uid) != NOID)
|
|
options |= OPT_UID;
|
|
if ((gid = key.gid) != NOID)
|
|
options |= OPT_GID;
|
|
}
|
|
switch (options & (OPT_UID|OPT_GID))
|
|
{
|
|
case OPT_UID:
|
|
s = ERROR_translate(0, 0, 0, " owner");
|
|
break;
|
|
case OPT_GID:
|
|
s = ERROR_translate(0, 0, 0, " group");
|
|
break;
|
|
case OPT_UID|OPT_GID:
|
|
s = ERROR_translate(0, 0, 0, " owner and group");
|
|
break;
|
|
default:
|
|
s = "";
|
|
break;
|
|
}
|
|
if (!(fts = fts_open(argv + 1, flags, NiL)))
|
|
{
|
|
error(ERROR_system(1), "%s: not found", argv[1]);
|
|
UNREACHABLE();
|
|
}
|
|
while (!sh_checksig(context) && (ent = fts_read(fts)))
|
|
switch (ent->fts_info)
|
|
{
|
|
case FTS_SL:
|
|
case FTS_SLNONE:
|
|
if (options & OPT_LCHOWN)
|
|
{
|
|
#if _lib_lchown
|
|
chownf = lchown;
|
|
op = "lchown";
|
|
goto commit;
|
|
#else
|
|
if (!(options & OPT_FORCE))
|
|
{
|
|
errno = ENOSYS;
|
|
error(ERROR_system(0), "%s: cannot change symlink owner/group", ent->fts_path);
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
case FTS_F:
|
|
case FTS_D:
|
|
anyway:
|
|
chownf = chown;
|
|
op = "chown";
|
|
commit:
|
|
if ((unsigned long)ent->fts_statp->st_ctime >= before)
|
|
break;
|
|
if (map)
|
|
{
|
|
options &= ~(OPT_UID|OPT_GID);
|
|
uid = gid = NOID;
|
|
keys[0].uid = keys[1].uid = ent->fts_statp->st_uid;
|
|
keys[0].gid = keys[2].gid = ent->fts_statp->st_gid;
|
|
i = 0;
|
|
do
|
|
{
|
|
if (m = (Map_t*)dtmatch(map, &keys[i]))
|
|
{
|
|
if (uid == NOID && m->to.uid != NOID)
|
|
{
|
|
uid = m->to.uid;
|
|
options |= OPT_UID;
|
|
}
|
|
if (gid == NOID && m->to.gid != NOID)
|
|
{
|
|
gid = m->to.gid;
|
|
options |= OPT_GID;
|
|
}
|
|
}
|
|
} while (++i < elementsof(keys) && (uid == NOID || gid == NOID));
|
|
}
|
|
else
|
|
{
|
|
if (!(options & OPT_UID))
|
|
uid = ent->fts_statp->st_uid;
|
|
if (!(options & OPT_GID))
|
|
gid = ent->fts_statp->st_gid;
|
|
}
|
|
if ((options & OPT_UNMAPPED) && (uid == NOID || gid == NOID))
|
|
{
|
|
if (uid == NOID && gid == NOID)
|
|
error(ERROR_warn(0), "%s: uid and gid not mapped", ent->fts_path);
|
|
else if (uid == NOID)
|
|
error(ERROR_warn(0), "%s: uid not mapped", ent->fts_path);
|
|
else
|
|
error(ERROR_warn(0), "%s: gid not mapped", ent->fts_path);
|
|
}
|
|
if (uid != ent->fts_statp->st_uid && uid != NOID || gid != ent->fts_statp->st_gid && gid != NOID)
|
|
{
|
|
if (options & (OPT_SHOW|OPT_VERBOSE))
|
|
{
|
|
if (options & OPT_TEST)
|
|
{
|
|
ent->fts_statp->st_uid = 0;
|
|
ent->fts_statp->st_gid = 0;
|
|
}
|
|
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);
|
|
}
|
|
if (!(options & OPT_SHOW) && (*chownf)(ent->fts_accpath, uid, gid) && !(options & OPT_FORCE))
|
|
error(ERROR_system(0), "%s: cannot change%s", ent->fts_path, s);
|
|
}
|
|
break;
|
|
case FTS_DC:
|
|
if (!(options & OPT_FORCE))
|
|
error(ERROR_warn(0), "%s: directory causes cycle", ent->fts_path);
|
|
break;
|
|
case FTS_DNR:
|
|
if (!(options & OPT_FORCE))
|
|
error(ERROR_system(0), "%s: cannot read directory", ent->fts_path);
|
|
goto anyway;
|
|
case FTS_DNX:
|
|
if (!(options & OPT_FORCE))
|
|
error(ERROR_system(0), "%s: cannot search directory", ent->fts_path);
|
|
goto anyway;
|
|
case FTS_NS:
|
|
if (!(options & OPT_FORCE))
|
|
error(ERROR_system(0), "%s: not found", ent->fts_path);
|
|
break;
|
|
}
|
|
fts_close(fts);
|
|
if (map)
|
|
dtclose(map);
|
|
return error_info.errors != 0;
|
|
}
|