177 lines
3.6 KiB
C
177 lines
3.6 KiB
C
/**
|
|
* \file lib/fileutil.c
|
|
*/
|
|
|
|
#include "system.h"
|
|
#include <rpmlib.h>
|
|
#include "misc.h"
|
|
#include "debug.h"
|
|
|
|
static struct {
|
|
const char *token;
|
|
int nargs;
|
|
} cmds[] = {
|
|
{ "mkdir", 1 },
|
|
#define RPMSYSCALL_MKDIR 0
|
|
{ "rmdir", 1 },
|
|
#define RPMSYSCALL_RMDIR 1
|
|
{ "mv", -2 },
|
|
#define RPMSYSCALL_MV 2
|
|
{ "symlink", 2 },
|
|
#define RPMSYSCALL_SYMLINK 3
|
|
{ "S_ISLNK", 1 }
|
|
#define RPMSYSCALL_ISLNK 4
|
|
};
|
|
int ncmds = sizeof(cmds)/sizeof(cmds[0]);
|
|
|
|
int rpmSyscall(const char * cmd, int noexec)
|
|
{
|
|
const char ** argv = NULL;
|
|
int argc;
|
|
struct stat st;
|
|
int i, j;
|
|
int rc;
|
|
|
|
rc = (noexec)
|
|
? poptParseArgvString(cmd, &argc, &argv)
|
|
: rpmGlob(cmd, &argc, &argv);
|
|
|
|
if (rc)
|
|
goto exit;
|
|
|
|
/* Check: argv[0] must be known. */
|
|
for (i = 0; i < ncmds; i++) {
|
|
if (!strcmp(cmds[i].token, argv[0]))
|
|
break;
|
|
}
|
|
if (i >= ncmds) {
|
|
rc = EPERM;
|
|
goto exit;
|
|
}
|
|
|
|
/* Check: must have exactly (or, if negative, at least) nargs. */
|
|
if (cmds[i].nargs >= 0 && argc != (cmds[i].nargs+1)) {
|
|
rc = EPERM;
|
|
goto exit;
|
|
} else if (argc < (1-cmds[i].nargs)) {
|
|
rc = EPERM;
|
|
goto exit;
|
|
}
|
|
|
|
/* Check: all args (except for 1st arg to symlink) must start with '/'. */
|
|
for (j = (i == RPMSYSCALL_SYMLINK ? 2 : 1); j < argc; j++) {
|
|
if (argv[j][0] != '/') {
|
|
rc = EPERM;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
/* Check: parse only with 2 or more args, last arg cannot be glob. */
|
|
if (noexec) {
|
|
if (argc > 2 && Glob_pattern_p(argv[argc-1], 0)) {
|
|
rc = 1;
|
|
goto exit;
|
|
}
|
|
rc = 0;
|
|
goto exit;
|
|
}
|
|
|
|
/* Execute only checks below */
|
|
|
|
switch (i) {
|
|
case RPMSYSCALL_MKDIR: /* mkdir */
|
|
{ mode_t mode = 0755;
|
|
rc = mkdir(argv[1], mode);
|
|
if (rc < 0) rc = errno;
|
|
if (stat(argv[1], &st) < 0) {
|
|
if (rc == 0) rc = errno;
|
|
goto exit;
|
|
}
|
|
if (!S_ISDIR(st.st_mode)) {
|
|
if (rc == 0) rc = ENOTDIR;
|
|
goto exit;
|
|
}
|
|
rc = 0;
|
|
} break;
|
|
case RPMSYSCALL_RMDIR: /* rmdir */
|
|
rc = rmdir(argv[1]);
|
|
if (rc < 0) rc = errno;
|
|
break;
|
|
case RPMSYSCALL_MV: /* mv */
|
|
{ dev_t dev;
|
|
const char * fn, * bn;
|
|
char * t;
|
|
size_t bnlen;
|
|
|
|
/* Check: if more than 2 args ... */
|
|
if (argc > 3) {
|
|
/* ... last arg must be existing directory ... */
|
|
if (stat(argv[argc-1], &st) < 0) {
|
|
if (rc == 0) rc = errno;
|
|
goto exit;
|
|
}
|
|
if (!S_ISDIR(st.st_mode)) {
|
|
if (rc == 0) rc = ENOTDIR;
|
|
goto exit;
|
|
}
|
|
/* ... and other args must all be on the same device. */
|
|
dev = st.st_dev;
|
|
bnlen = 0;
|
|
for (j = 1; j < (argc-1); j++) {
|
|
if (stat(argv[j], &st) < 0) {
|
|
if (rc == 0) rc = errno;
|
|
goto exit;
|
|
}
|
|
if (dev != st.st_dev) {
|
|
if (rc == 0) rc = EXDEV;
|
|
goto exit;
|
|
}
|
|
bn = strrchr(argv[j], '/');
|
|
if (bn) {
|
|
int k = strlen(bn);
|
|
if (k > bnlen) bnlen = k;
|
|
}
|
|
}
|
|
/* Everything looks OK, so do the renames. */
|
|
fn = t = alloca(strlen(argv[argc-1])+2+bnlen);
|
|
t = stpcpy(t, argv[argc-1]);
|
|
if (t[-1] != '/') *t++ = '/';
|
|
for (j = 1; j < (argc-1); j++) {
|
|
bn = strrchr(argv[j], '/');
|
|
(void) stpcpy(t, bn+1);
|
|
rc = rename(argv[j], fn);
|
|
if (rc < 0) {
|
|
rc = errno;
|
|
goto exit;
|
|
}
|
|
}
|
|
} else { /* Otherwise exactly 2 args. */
|
|
rc = rename(argv[1], argv[2]);
|
|
if (rc < 0) rc = errno;
|
|
}
|
|
} break;
|
|
case RPMSYSCALL_SYMLINK: /* symlink */
|
|
rc = symlink(argv[1], argv[2]);
|
|
if (rc < 0) rc = errno;
|
|
break;
|
|
case RPMSYSCALL_ISLNK: /* stat(2) with S_ISLNK */
|
|
if (stat(argv[1], &st) < 0) {
|
|
if (rc == 0) rc = errno;
|
|
goto exit;
|
|
}
|
|
if (S_ISLNK(st.st_mode)) {
|
|
if (rc == 0) rc = EPERM;
|
|
goto exit;
|
|
}
|
|
rc = 0;
|
|
break;
|
|
default:
|
|
rc = EPERM;
|
|
break;
|
|
}
|
|
|
|
exit:
|
|
argv = _free(argv);
|
|
return rc;
|
|
}
|