Mercurial > illumos > illumos-gate
changeset 3855:df4146229995
6296436 /usr/bin/rm has slowed down
6478856 rm(1) fails to delete subdirectories if the current directory is non-readable
6478917 recursive rm operations should be performed via file descriptors
6500134 v_path construction can consume boundless amounts of memory
6501452 *rm* rm can loop forever and delete the wrong files.
author | sn199410 |
---|---|
date | Mon, 19 Mar 2007 14:20:27 -0700 |
parents | 406e18bafdbd |
children | 34d23baad159 |
files | usr/src/cmd/rm/Makefile usr/src/cmd/rm/rm.c usr/src/cmd/truss/systable.c usr/src/lib/libc/amd64/Makefile usr/src/lib/libc/i386/Makefile.com usr/src/lib/libc/port/llib-lc usr/src/lib/libc/port/mapfile-vers usr/src/lib/libc/port/sys/faccessat.c usr/src/lib/libc/sparc/Makefile usr/src/lib/libc/sparcv9/Makefile usr/src/uts/common/fs/vnode.c usr/src/uts/common/syscall/access.c usr/src/uts/common/syscall/fsat.c |
diffstat | 13 files changed, 574 insertions(+), 855 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/cmd/rm/Makefile Mon Mar 19 11:35:26 2007 -0700 +++ b/usr/src/cmd/rm/Makefile Mon Mar 19 14:20:27 2007 -0700 @@ -21,7 +21,7 @@ # #ident "%Z%%M% %I% %E% SMI" # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # @@ -33,7 +33,6 @@ CFLAGS += $(CCVERBOSE) $(XPG4) := CFLAGS += -DXPG4 CPPFLAGS += -D_FILE_OFFSET_BITS=64 -LDLIBS += -lcmdutils .KEEP_STATE:
--- a/usr/src/cmd/rm/rm.c Mon Mar 19 11:35:26 2007 -0700 +++ b/usr/src/cmd/rm/rm.c Mon Mar 19 14:20:27 2007 -0700 @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -33,86 +33,69 @@ * rm [-fiRr] file ... */ -#include <stdio.h> -#include <fcntl.h> -#include <string.h> -#include <sys/types.h> +#include <sys/param.h> #include <sys/stat.h> #include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <langinfo.h> #include <limits.h> #include <locale.h> -#include <langinfo.h> -#include <unistd.h> +#include <stdarg.h> +#include <stdio.h> #include <stdlib.h> -#include <errno.h> -#include <sys/resource.h> -#include <sys/avl.h> -#include <libcmdutils.h> +#include <string.h> +#include <unistd.h> +#include <values.h> -#define ARGCNT 5 /* Number of arguments */ -#define CHILD 0 -#define DIRECTORY ((buffer.st_mode&S_IFMT) == S_IFDIR) -#define SYMLINK ((buffer.st_mode&S_IFMT) == S_IFLNK) -#define FAIL -1 -#define MAXFORK 100 /* Maximum number of forking attempts */ -#define NAMESIZE MAXNAMLEN + 1 /* "/" + (file name size) */ -#define TRUE 1 -#define FALSE 0 -#define WRITE 02 -#define SEARCH 07 +#define E_OK 010 /* make __accessat() use effective ids */ -static int errcode; -static int interactive, recursive, silent; /* flags for command line options */ +#define DIR_CANTCLOSE 1 -static int rm(char *, int); -static int undir(char *, int, dev_t, ino_t); -static int yes(void); -static int mypath(dev_t, ino_t); - -static char yeschr[SCHAR_MAX + 2]; -static char nochr[SCHAR_MAX + 2]; - -static char *fullpath; -static int initdirfd; +static struct stat rootdir; -static void push_name(char *name, int first); -static int pop_name(int first); -static void force_chdir(char *); -static void ch_dir(char *); -static char *get_filename(char *name); -static void chdir_init(void); -static void check_initdir(void); -static void cleanup(void); - -static char *cwd; /* pathname of init dir, from getcwd() */ -static rlim_t maxfiles; /* maximum number of open files */ -static int first_dir = 1; /* flag set when first trying to remove a dir */ - /* flag set when can't get dev/inode of a parent dir */ -static int parent_err = 0; -static avl_tree_t *tree; /* tree to keep track of nodes visited */ - -struct dir_id { - dev_t dev; - ino_t inode; - struct dir_id *next; +struct dlist { + int fd; /* Stores directory fd */ + int flags; /* DIR_* Flags */ + DIR *dp; /* Open directory (opened with fd) */ + long diroff; /* Saved directory offset when closing */ + struct dlist *up; /* Up one step in the tree (toward "/") */ + struct dlist *down; /* Down one step in the tree */ + ino_t ino; /* st_ino of directory */ + dev_t dev; /* st_dev of directory */ + int pathend; /* Offset of name end in the pathbuffer */ }; - /* - * initdir is the first of a linked list of structures - * containing unique identifying device and inode numbers for - * each directory, from the initial dir up to the root. - * current_dir is a pointer to the most recent directory pushed - * on during a recursive rm() call. - */ -static struct dir_id initdir, *current_dir; +static struct dlist top = { + (int)AT_FDCWD, + DIR_CANTCLOSE, +}; + +static char yeschr[SCHAR_MAX + 2]; +static char nochr[SCHAR_MAX + 2]; + +static struct dlist *cur, *rec; + +static int rm(const char *, struct dlist *); +static int confirm(FILE *, const char *, ...); +static void memerror(void); +static int checkdir(struct dlist *, struct dlist *); +static int errcnt; +static boolean_t silent, interactive, recursive, ontty; + +static char *pathbuf; +static size_t pathbuflen; + +static int maxfds = MAXINT; +static int nfds; + +extern int __accessat(int, const char *, int); int -main(int argc, char *argv[]) +main(int argc, char **argv) { - extern int optind; - int errflg = 0; - int c; - struct rlimit rl; + int errflg = 0; + int c; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ @@ -126,20 +109,20 @@ while ((c = getopt(argc, argv, "frRi")) != EOF) switch (c) { case 'f': - silent = TRUE; + silent = B_TRUE; #ifdef XPG4 - interactive = FALSE; + interactive = B_FALSE; #endif break; case 'i': - interactive = TRUE; + interactive = B_TRUE; #ifdef XPG4 - silent = FALSE; + silent = B_FALSE; #endif break; case 'r': case 'R': - recursive = TRUE; + recursive = B_TRUE; break; case '?': errflg = 1; @@ -161,468 +144,426 @@ argv = &argv[optind]; if ((argc < 1 && !silent) || errflg) { + (void) fprintf(stderr, gettext("usage: rm [-fiRr] file ...\n")); + exit(2); + } + + ontty = isatty(STDIN_FILENO) != 0; + + if (recursive && stat("/", &rootdir) != 0) { (void) fprintf(stderr, - gettext("usage: rm [-fiRr] file ...\n")); + gettext("rm: cannot stat root directory: %s\n"), + strerror(errno)); exit(2); } - if (getrlimit(RLIMIT_NOFILE, &rl)) { - perror("getrlimit"); - exit(2); - } else - maxfiles = rl.rlim_cur - 2; - - while (argc-- > 0) { - tree = NULL; - /* Retry if rm() fails due to bad chdir */ - while (rm(*argv, 1) < 0) + for (; *argv != NULL; argv++) { + char *p = strrchr(*argv, '/'); + if (p == NULL) + p = *argv; + else + p = p + 1; + if (strcmp(p, ".") == 0 || strcmp(p, "..") == 0) { + (void) fprintf(stderr, + gettext("rm of %s is not allowed\n"), *argv); + errcnt++; + continue; + } + /* Retry when we can't walk back up. */ + while (rm(*argv, rec = cur = &top) != 0) ; - argv++; - destroy_tree(tree); } - cleanup(); - return (errcode ? 2 : 0); - /* NOTREACHED */ + return (errcnt != 0 ? 2 : 0); +} + +static void +pushfilename(const char *fname) +{ + char *p; + const char *q = fname; + + if (cur == &top) { + p = pathbuf; + } else { + p = pathbuf + cur->up->pathend; + *p++ = '/'; + } + while (*q != '\0') { + if (p - pathbuf + 2 >= pathbuflen) { + char *np; + pathbuflen += MAXPATHLEN; + np = realloc(pathbuf, pathbuflen); + if (np == NULL) + memerror(); + p = np + (p - pathbuf); + pathbuf = np; + } + *p++ = *q++; + } + *p = '\0'; + cur->pathend = p - pathbuf; +} + +static void +closeframe(struct dlist *frm) +{ + if (frm->dp != NULL) { + (void) closedir(frm->dp); + nfds--; + frm->dp = NULL; + frm->fd = -1; + } +} + +static int +reclaim(void) +{ + while (rec != NULL && (rec->flags & DIR_CANTCLOSE) != 0) + rec = rec->down; + if (rec == NULL || rec == cur || rec->dp == NULL) + return (-1); + rec->diroff = telldir(rec->dp); + closeframe(rec); + rec = rec->down; + return (0); +} + +static void +pushdir(struct dlist *frm) +{ + frm->up = cur; + frm->down = NULL; + cur->down = frm; + cur = frm; } static int -rm(char *path, int first) +opendirat(int dirfd, const char *entry, struct dlist *frm) +{ + int fd; + + if (nfds >= maxfds) + (void) reclaim(); + + while ((fd = openat(dirfd, entry, O_RDONLY|O_NONBLOCK)) == -1 && + errno == EMFILE) { + if (nfds < maxfds) + maxfds = nfds; + if (reclaim() != 0) + return (-1); + } + if (fd < 0) + return (-1); + frm->fd = fd; + frm->dp = fdopendir(fd); + if (frm->dp == NULL) { + (void) close(fd); + return (-1); + } + nfds++; + return (0); +} + +/* + * Since we never pop the top frame, cur->up can never be NULL. + * If we pop beyond a frame we closed, we try to reopen "..". + */ +static int +popdir(boolean_t noerror) { - struct stat buffer; - char *filepath; - char *p; - char resolved_path[PATH_MAX]; + struct stat buf; + int ret = noerror ? 0 : -1; + pathbuf[cur->up->pathend] = '\0'; + + if (noerror && cur->up->fd == -1) { + rec = cur->up; + if (opendirat(cur->fd, "..", rec) != 0 || + fstat(rec->fd, &buf) != 0) { + (void) fprintf(stderr, + gettext("rm: cannot reopen %s: %s\n"), + pathbuf, strerror(errno)); + exit(2); + } + if (rec->ino != buf.st_ino || rec->dev != buf.st_dev) { + (void) fprintf(stderr, gettext("rm: WARNING: " + "The directory %s was moved or linked to " + "another directory during the execution of rm\n"), + pathbuf); + closeframe(rec); + ret = -1; + } else { + /* If telldir failed, we take it from the top. */ + if (rec->diroff != -1) + seekdir(rec->dp, rec->diroff); + } + } else if (rec == cur) + rec = cur->up; + closeframe(cur); + cur = cur->up; + cur->down = NULL; + return (ret); +} + +/* + * The stack frame of this function is minimized so that we can + * recurse quite a bit before we overflow the stack; around + * 30,000-40,000 nested directories can be removed with the default + * stack limit. + */ +static int +rm(const char *entry, struct dlist *caller) +{ + struct dlist frame; + int flag; + struct stat temp; + struct dirent *dent; + int err; /* - * Check file to see if it exists. + * Construct the pathname: note that the entry may live in memory + * allocated by readdir and that after return from recursion + * the memory is no longer valid. So after the recursive rm() + * call, we use the global pathbuf instead of the entry argument. */ - if (lstat(path, &buffer) == FAIL) { + pushfilename(entry); + + if (fstatat(caller->fd, entry, &temp, AT_SYMLINK_NOFOLLOW) != 0) { if (!silent) { - perror(path); - ++errcode; + (void) fprintf(stderr, "rm: %s: %s\n", pathbuf, + strerror(errno)); + errcnt++; } return (0); } - /* prevent removal of / but allow removal of sym-links */ - if (!S_ISLNK(buffer.st_mode) && realpath(path, resolved_path) != NULL && - strcmp(resolved_path, "/") == 0) { - (void) fprintf(stderr, - gettext("rm of %s is not allowed\n"), resolved_path); - errcode++; - return (0); - } - - /* prevent removal of . or .. (directly) */ - if (p = strrchr(path, '/')) - p++; - else - p = path; - if (strcmp(".", p) == 0 || strcmp("..", p) == 0) { - (void) fprintf(stderr, - gettext("rm of %s is not allowed\n"), path); - errcode++; - return (0); - } - /* - * If it's a directory, remove its contents. - */ - if (DIRECTORY) { + if (S_ISDIR(temp.st_mode)) { /* * If "-r" wasn't specified, trying to remove directories * is an error. */ if (!recursive) { (void) fprintf(stderr, - gettext("rm: %s is a directory\n"), path); - ++errcode; + gettext("rm: %s is a directory\n"), pathbuf); + errcnt++; + return (0); + } + + if (temp.st_ino == rootdir.st_ino && + temp.st_dev == rootdir.st_dev) { + (void) fprintf(stderr, + gettext("rm of %s is not allowed\n"), "/"); + errcnt++; + return (0); + } + /* + * TRANSLATION_NOTE - The following message will contain the + * first character of the strings for "yes" and "no" defined + * in the file "nl_langinfo.po". After substitution, the + * message will appear as follows: + * rm: examine files in directory <directoryname> (y/n)? + * where <directoryname> is the directory to be removed + * + */ + if (interactive && !confirm(stderr, + gettext("rm: examine files in directory %s (%s/%s)? "), + pathbuf, yeschr, nochr)) { return (0); } - if (first_dir) { - check_initdir(); - current_dir = NULL; - first_dir = 0; + frame.dev = temp.st_dev; + frame.ino = temp.st_ino; + frame.flags = 0; + flag = AT_REMOVEDIR; + +#ifdef XPG4 + /* + * XCU4 and POSIX.2: If not interactive, check to see whether + * or not directory is readable or writable and if not, + * prompt user for response. + */ + if (ontty && !interactive && !silent && + __accessat(caller->fd, entry, W_OK|X_OK|E_OK) != 0 && + !confirm(stderr, + gettext("rm: examine files in directory %s (%s/%s)? "), + pathbuf, yeschr, nochr)) { + return (0); + } +#endif + if (opendirat(caller->fd, entry, &frame) == -1) { + err = errno; + + if (interactive) { + /* + * Print an error message that + * we could not read the directory + * as the user wanted to examine + * files in the directory. Only + * affect the error status if + * user doesn't want to remove the + * directory as we still may be able + * remove the directory successfully. + */ + (void) fprintf(stderr, gettext( + "rm: cannot read directory %s: %s\n"), + pathbuf, strerror(err)); + +/* + * TRANSLATION_NOTE - The following message will contain the + * first character of the strings for "yes" and "no" defined + * in the file "nl_langinfo.po". After substitution, the + * message will appear as follows: + * rm: remove <filename> (y/n)? + * For example, in German, this will appear as + * rm: löschen <filename> (j/n)? + * where j=ja, n=nein, <filename>=the file to be removed + */ + if (!confirm(stderr, + gettext("rm: remove %s (%s/%s)? "), + pathbuf, yeschr, nochr)) { + errcnt++; + return (0); + } + } + /* If it's empty we may still be able to rm it */ + if (unlinkat(caller->fd, entry, flag) == 0) + return (0); + if (interactive) + err = errno; + (void) fprintf(stderr, + interactive ? + gettext("rm: Unable to remove directory %s: %s\n") : + gettext("rm: cannot read directory %s: %s\n"), + pathbuf, strerror(err)); + errcnt++; + return (0); } - return (undir(path, first, buffer.st_dev, buffer.st_ino)); - } + /* + * There is a race condition here too; if we open a directory + * we have to make sure it's still the same directory we + * stat'ed and checked against root earlier. Let's check. + */ + if (fstat(frame.fd, &temp) != 0 || + frame.ino != temp.st_ino || + frame.dev != temp.st_dev) { + (void) fprintf(stderr, + gettext("rm: %s: directory renamed\n"), pathbuf); + closeframe(&frame); + errcnt++; + return (0); + } + + if (caller != &top) { + if (checkdir(caller, &frame) != 0) { + closeframe(&frame); + goto unlinkit; + } + } + pushdir(&frame); - filepath = get_filename(path); + /* + * rm() only returns -1 if popdir failed at some point; + * frame.dp is no longer reliable and we must drop out. + */ + while ((dent = readdir(frame.dp)) != NULL) { + if (strcmp(dent->d_name, ".") == 0 || + strcmp(dent->d_name, "..") == 0) + continue; + + if (rm(dent->d_name, &frame) != 0) + break; + } + if (popdir(dent == NULL) != 0) + return (-1); + + /* + * We recursed and the subdirectory may have set the CANTCLOSE + * flag; we need to clear it except for &top. + * Recursion may have invalidated entry because of closedir(). + */ + if (caller != &top) { + caller->flags &= ~DIR_CANTCLOSE; + entry = &pathbuf[caller->up->pathend + 1]; + } + } else { + flag = 0; + } +unlinkit: /* * If interactive, ask for acknowledgement. - * - * TRANSLATION_NOTE - The following message will contain the - * first character of the strings for "yes" and "no" defined - * in the file "nl_langinfo.po". After substitution, the - * message will appear as follows: - * rm: remove <filename> (y/n)? - * For example, in German, this will appear as - * rm: löschen <filename> (j/n)? - * where j=ja, n=nein, <filename>=the file to be removed - * */ - - if (interactive) { - (void) fprintf(stderr, gettext("rm: remove %s (%s/%s)? "), - filepath, yeschr, nochr); - if (!yes()) { - free(filepath); + if (!confirm(stderr, gettext("rm: remove %s (%s/%s)? "), + pathbuf, yeschr, nochr)) { return (0); } - } else if (!silent) { + } else if (!silent && flag == 0) { /* * If not silent, and stdin is a terminal, and there's * no write access, and the file isn't a symbolic link, - * ask for permission. + * ask for permission. If flag is set, then we know it's + * a directory so we skip this test as it was done above. * * TRANSLATION_NOTE - The following message will contain the * first character of the strings for "yes" and "no" defined * in the file "nl_langinfo.po". After substitution, the * message will appear as follows: - * rm: <filename>: override protection XXX (y/n)? + * rm: <filename>: override protection XXX (y/n)? * where XXX is the permission mode bits of the file in octal * and <filename> is the file to be removed * */ - if (!SYMLINK && access(path, W_OK) == FAIL && - isatty(fileno(stdin))) { - (void) printf( - gettext("rm: %s: override protection %o (%s/%s)? "), - filepath, buffer.st_mode & 0777, yeschr, nochr); - /* - * If permission isn't given, skip the file. - */ - if (!yes()) { - free(filepath); - return (0); - } + if (ontty && !S_ISLNK(temp.st_mode) && + __accessat(caller->fd, entry, W_OK|E_OK) != 0 && + !confirm(stdout, + gettext("rm: %s: override protection %o (%s/%s)? "), + pathbuf, temp.st_mode & 0777, yeschr, nochr)) { + return (0); } } - /* - * If the unlink fails, inform the user. For /usr/bin/rm, only inform - * the user if interactive or not silent. - * If unlink fails with errno = ENOENT because file was removed - * in between the lstat call and unlink don't inform the user and - * don't change errcode. - */ + if (unlinkat(caller->fd, entry, flag) != 0) { + err = errno; + if (err == ENOENT) + return (0); - if (unlink(path) == FAIL) { - if (errno == ENOENT) { - free(filepath); - return (0); + if (flag != 0) { + if (err == EINVAL) { + (void) fprintf(stderr, gettext( + "rm: Cannot remove any directory in the " + "path of the current working directory\n" + "%s\n"), pathbuf); + } else { + if (err == EEXIST) + err = ENOTEMPTY; + (void) fprintf(stderr, + gettext("rm: Unable to remove directory %s:" + " %s\n"), pathbuf, strerror(err)); + } + } else { +#ifndef XPG4 + if (!silent || interactive) { +#endif + + (void) fprintf(stderr, + gettext("rm: %s not removed: %s\n"), + pathbuf, strerror(err)); +#ifndef XPG4 + } +#endif } -#ifndef XPG4 - if (!silent || interactive) { -#endif - (void) fprintf(stderr, - gettext("rm: %s not removed: "), filepath); - perror(""); -#ifndef XPG4 - } -#endif - ++errcode; + errcnt++; } - - free(filepath); return (0); } static int -undir(char *path, int first, dev_t dev, ino_t ino) -{ - char *newpath; - DIR *name; - struct dirent *direct; - int ismypath; - int ret; - int chdir_failed = 0; - int bad_chdir = 0; - size_t len; - - push_name(path, first); - - /* - * If interactive and this file isn't in the path of the - * current working directory, ask for acknowledgement. - * - * TRANSLATION_NOTE - The following message will contain the - * first character of the strings for "yes" and "no" defined - * in the file "nl_langinfo.po". After substitution, the - * message will appear as follows: - * rm: examine files in directory <directoryname> (y/n)? - * where <directoryname> is the directory to be removed - * - */ - ismypath = mypath(dev, ino); - if (interactive) { - (void) fprintf(stderr, - gettext("rm: examine files in directory %s (%s/%s)? "), - fullpath, yeschr, nochr); - /* - * If the answer is no, skip the directory. - */ - if (!yes()) - return (pop_name(first)); - } - -#ifdef XPG4 - /* - * XCU4 and POSIX.2: If not interactive and file is not in the - * path of the current working directory, check to see whether - * or not directory is readable or writable and if not, - * prompt user for response. - */ - if (!interactive && !ismypath && - (access(path, W_OK|X_OK) == FAIL) && isatty(fileno(stdin))) { - if (!silent) { - (void) fprintf(stderr, - gettext( - "rm: examine files in directory %s (%s/%s)? "), - fullpath, yeschr, nochr); - /* - * If the answer is no, skip the directory. - */ - if (!yes()) - return (pop_name(first)); - } - } -#endif - - /* - * Add this node to the search tree so we don't - * get into a endless loop. If the add fails then - * we have visited this node before. - */ - ret = add_tnode(&tree, dev, ino); - if (ret != 1) { - if (ret == 0) { - (void) fprintf(stderr, - gettext("rm: cycle detected for %s\n"), - fullpath); - } else if (ret == -1) { - perror("rm"); - } - errcode++; - return (pop_name(first)); - } - - /* - * Open the directory for reading. - */ - if ((name = opendir(path)) == NULL) { - int saveerrno = errno; - - /* - * If interactive, ask for acknowledgement. - */ - if (interactive) { - /* - * Print an error message that - * we could not read the directory - * as the user wanted to examine - * files in the directory. Only - * affect the error status if - * user doesn't want to remove the - * directory as we still may be able - * remove the directory successfully. - */ - (void) fprintf(stderr, gettext( - "rm: cannot read directory %s: "), - fullpath); - errno = saveerrno; - perror(""); - (void) fprintf(stderr, gettext( - "rm: remove %s: (%s/%s)? "), - fullpath, yeschr, nochr); - if (!yes()) { - ++errcode; - return (pop_name(first)); - } - } - - /* - * If the directory is empty, we may be able to - * go ahead and remove it. - */ - if (rmdir(path) == FAIL) { - if (interactive) { - int rmdirerr = errno; - (void) fprintf(stderr, gettext( - "rm: Unable to remove directory %s: "), - fullpath); - errno = rmdirerr; - perror(""); - } else { - (void) fprintf(stderr, gettext( - "rm: cannot read directory %s: "), - fullpath); - errno = saveerrno; - perror(""); - } - ++errcode; - } - - /* Continue to next file/directory rather than exit */ - return (pop_name(first)); - } - - /* - * XCU4 requires that rm -r descend the directory - * hierarchy without regard to PATH_MAX. If we can't - * chdir() do not increment error counter and do not - * print message. - * - * However, if we cannot chdir because someone has taken away - * execute access we may still be able to delete the directory - * if it's empty. The old rm could do this. - */ - - if (chdir(path) == -1) { - chdir_failed = 1; - } - - /* - * Read every directory entry. - */ - while ((direct = readdir(name)) != NULL) { - /* - * Ignore "." and ".." entries. - */ - if (strcmp(direct->d_name, ".") == 0 || - strcmp(direct->d_name, "..") == 0) - continue; - /* - * Try to remove the file. - */ - len = strlen(direct->d_name) + 1; - if (chdir_failed) { - len += strlen(path) + 2; - } - - newpath = malloc(len); - if (newpath == NULL) { - (void) fprintf(stderr, - gettext("rm: Insufficient memory.\n")); - cleanup(); - exit(1); - } - - if (!chdir_failed) { - (void) strcpy(newpath, direct->d_name); - } else { - (void) snprintf(newpath, len, "%s/%s", - path, direct->d_name); - } - - - /* - * If a spare file descriptor is available, just call the - * "rm" function with the file name; otherwise close the - * directory and reopen it when the child is removed. - */ - if (name->dd_fd >= maxfiles) { - (void) closedir(name); - if (rm(newpath, 0) < 0) - bad_chdir = -1; - if (!chdir_failed) - name = opendir("."); - else - name = opendir(path); - if (name == NULL) { - (void) fprintf(stderr, - gettext("rm: cannot read directory %s: "), - fullpath); - perror(""); - cleanup(); - exit(2); - } - } else if (rm(newpath, 0) < 0) - bad_chdir = -1; - - free(newpath); - if (bad_chdir) - break; - } - - /* - * Close the directory we just finished reading. - */ - (void) closedir(name); - if (bad_chdir) - return (-1); - - /* - * The contents of the directory have been removed. If the - * directory itself is in the path of the current working - * directory, don't try to remove it. - * When the directory itself is the current working directory, mypath() - * has a return code == 2. - * - * XCU4: Because we've descended the directory hierarchy in order - * to avoid PATH_MAX limitation, we must now start ascending - * one level at a time and remove files/directories. - */ - - if (!chdir_failed) { - if (first) - chdir_init(); - else if (chdir("..") == -1) { - (void) fprintf(stderr, - gettext("rm: cannot change to parent of " - "directory %s: "), - fullpath); - perror(""); - cleanup(); - exit(2); - } - } - - switch (ismypath) { - case 3: - return (pop_name(first)); - case 2: - (void) fprintf(stderr, - gettext("rm: Cannot remove any directory in the path " - "of the current working directory\n%s\n"), fullpath); - ++errcode; - return (pop_name(first)); - case 1: - ++errcode; - return (pop_name(first)); - case 0: - break; - } - - /* - * If interactive, ask for acknowledgement. - */ - if (interactive) { - (void) fprintf(stderr, gettext("rm: remove %s: (%s/%s)? "), - fullpath, yeschr, nochr); - if (!yes()) - return (pop_name(first)); - } - if (rmdir(path) == FAIL) { - (void) fprintf(stderr, - gettext("rm: Unable to remove directory %s: "), - fullpath); - perror(""); - ++errcode; - } - return (pop_name(first)); -} - - -static int yes(void) { - int i, b; - char ans[SCHAR_MAX + 1]; + int i, b; + char ans[SCHAR_MAX + 1]; for (i = 0; ; i++) { b = getchar(); @@ -631,7 +572,7 @@ break; } if (i < SCHAR_MAX) - ans[i] = b; + ans[i] = (char)b; } if (i >= SCHAR_MAX) { i = SCHAR_MAX; @@ -642,387 +583,54 @@ return (1); } - static int -mypath(dev_t dev, ino_t ino) -{ - struct dir_id *curdir; - - /* - * Check to see if this is our current directory - * Indicated by return 2; - */ - if (dev == initdir.dev && ino == initdir.inode) { - return (2); - } - - curdir = initdir.next; - - while (curdir != NULL) { - /* - * If we find a match, the directory (dev, ino) passed to - * mypath() is an ancestor of ours. Indicated by return 3. - */ - if (curdir->dev == dev && curdir->inode == ino) - return (3); - curdir = curdir->next; - } - /* - * parent_err indicates we couldn't stat or chdir to - * one of our parent dirs, so the linked list of dir_id structs - * is incomplete - */ - if (parent_err) { -#ifndef XPG4 - if (!silent || interactive) { -#endif - (void) fprintf(stderr, gettext("rm: cannot determine " - "if this is an ancestor of the current " - "working directory\n%s\n"), fullpath); -#ifndef XPG4 - } -#endif - /* assume it is. least dangerous */ - return (1); - } - return (0); -} - -static int maxlen; -static int curlen; - -static char * -get_filename(char *name) -{ - char *path; - size_t len; - - if (fullpath == NULL || *fullpath == '\0') { - path = strdup(name); - if (path == NULL) { - (void) fprintf(stderr, - gettext("rm: Insufficient memory.\n")); - cleanup(); - exit(1); - } - } else { - len = strlen(fullpath) + strlen(name) + 2; - path = malloc(len); - if (path == NULL) { - (void) fprintf(stderr, - gettext("rm: Insufficient memory.\n")); - cleanup(); - exit(1); - } - (void) snprintf(path, len, "%s/%s", fullpath, name); - } - return (path); -} - -static void -push_name(char *name, int first) +confirm(FILE *fp, const char *q, ...) { - int namelen; - struct stat buffer; - struct dir_id *newdir; - - namelen = strlen(name) + 1; /* 1 for "/" */ - if ((curlen + namelen) >= maxlen) { - maxlen += PATH_MAX; - fullpath = (char *)realloc(fullpath, (size_t)(maxlen + 1)); - } - if (first) { - (void) strcpy(fullpath, name); - } else { - (void) strcat(fullpath, "/"); - (void) strcat(fullpath, name); - } - curlen = strlen(fullpath); - - if (stat(".", &buffer) == -1) { - (void) fprintf(stderr, - gettext("rm: cannot stat current directory: ")); - perror(""); - exit(2); - } - if ((newdir = malloc(sizeof (struct dir_id))) == NULL) { - (void) fprintf(stderr, - gettext("rm: Insufficient memory.\n")); - cleanup(); - exit(1); - } - - newdir->dev = buffer.st_dev; - newdir->inode = buffer.st_ino; - newdir->next = current_dir; - current_dir = newdir; -} + va_list ap; -static int -pop_name(int first) -{ - int retval = 0; - char *slash; - struct stat buffer; - struct dir_id *remove_dir; - - if (first) { - *fullpath = '\0'; - return (0); - } - slash = strrchr(fullpath, '/'); - if (slash) - *slash = '\0'; - else - *fullpath = '\0'; - curlen = strlen(fullpath); - - if (stat(".", &buffer) == -1) { - (void) fprintf(stderr, - gettext("rm: cannot stat current directory: ")); - perror(""); - exit(2); - } - - /* - * For each pop operation, verify that the device and inode numbers - * of "." match the numbers recorded before the chdir was done into - * the directory. If they do not match, it is an indication of - * possible malicious activity and rm has chdir to an unintended - * directory - */ - if ((current_dir->inode != buffer.st_ino) || (current_dir->dev != - buffer.st_dev)) { - (void) fprintf(stderr, gettext("rm: WARNING: " - "A subdirectory of %s was moved or linked to " - "another directory during the execution of rm\n"), - fullpath); - retval = -1; - } - remove_dir = current_dir; - current_dir = current_dir->next; - free(remove_dir); - return (retval); + va_start(ap, q); + (void) vfprintf(fp, q, ap); + va_end(ap); + return (yes()); } static void -force_chdir(char *dirname) +memerror(void) { - char *pathname, *mp, *tp; - - /* use pathname instead of dirname, so dirname won't be modified */ - if ((pathname = strdup(dirname)) == NULL) { - (void) fprintf(stderr, gettext("rm: strdup: ")); - perror(""); - cleanup(); - exit(2); - } - - /* pathname is an absolute full path from getcwd() */ - mp = pathname; - while (mp) { - tp = strchr(mp, '/'); - if (strlen(mp) >= PATH_MAX) { - /* - * after the first iteration through this - * loop, the below will NULL out the '/' - * which follows the first dir on pathname - */ - *tp = 0; - tp++; - if (*mp == NULL) - ch_dir("/"); - else - /* - * mp points to the start of a dirname, - * terminated by NULL, so ch_dir() - * here will move down one directory - */ - ch_dir(mp); - /* - * reset mp to the start of the dirname - * which follows the one we just chdir'd to - */ - mp = tp; - continue; /* probably can remove this */ - } else { - ch_dir(mp); - break; - } - } - free(pathname); -} - -static void -ch_dir(char *dirname) -{ - if (chdir(dirname) == -1) { - (void) fprintf(stderr, - gettext("rm: cannot change to %s directory: "), dirname); - perror(""); - cleanup(); - exit(2); - } -} - -static void -chdir_init(void) -{ - /* - * Go back to init dir--the dir from where rm was executed--using - * one of two methods, depending on which method works - * for the given permissions of the init dir and its - * parent directories. - */ - if (initdirfd != -1) { - if (fchdir(initdirfd) == -1) { - (void) fprintf(stderr, - gettext("rm: cannot change to starting " - "directory: ")); - perror(""); - cleanup(); - exit(2); - } - } else { - if (strlen(cwd) < PATH_MAX) - ch_dir(cwd); - else - force_chdir(cwd); - } + (void) fprintf(stderr, gettext("rm: Insufficient memory.\n")); + exit(1); } /* - * check_initdir - - * is only called the first time rm tries to - * remove a directory. It saves the current directory, i.e., - * init dir, so we can go back to it after traversing elsewhere. - * It also saves all the device and inode numbers of each - * dir from the initial dir back to the root in a linked list, so we - * can later check, via mypath(), if we are trying to remove our current - * dir or an ancestor. + * If we can't stat "..", it's either not there or we can't search + * the current directory; in that case we can't return back through + * "..", so we need to keep the parent open. + * Check that we came from "..", if not then this directory entry is an + * additional link and there is risk of a filesystem cycle and we also + * can't go back up through ".." and we keep the directory open. */ -static void -check_initdir(void) +static int +checkdir(struct dlist *caller, struct dlist *frmp) { - int size; /* size allocated for pathname of init dir (cwd) */ - struct stat buffer; - struct dir_id *lastdir, *curdir; + struct stat up; + struct dlist *ptr; - /* - * We need to save where we currently are (the "init dir") so - * we can return after traversing down directories we're - * removing. Two methods are attempted: - * - * 1) open() the initial dir so we can use the fd - * to fchdir() back. This requires read permission - * on the initial dir. - * - * 2) getcwd() so we can chdir() to go back. This - * requires search (x) permission on the init dir, - * and read and search permission on all parent dirs. Also, - * getcwd() will not work if the init dir is > 341 - * directories deep (see open bugid 4033182 - getcwd needs - * to work for pathnames of any depth). - * - * If neither method works, we can't remove any directories - * and rm will fail. - * - * For future enhancement, a possible 3rd option to use - * would be to fork a process to remove a directory, - * eliminating the need to chdir back to the initial directory - * and eliminating the permission restrictions on the initial dir - * or its parent dirs. - */ - initdirfd = open(".", O_RDONLY); - if (initdirfd == -1) { - size = PATH_MAX; - while ((cwd = getcwd(NULL, size)) == NULL) { - if (errno == ERANGE) { - size = PATH_MAX + size; - continue; - } else { - (void) fprintf(stderr, - gettext("rm: cannot open starting " - "directory: ")); - perror("pwd"); - exit(2); - } - } + if (fstatat(frmp->fd, "..", &up, 0) != 0) { + caller->flags |= DIR_CANTCLOSE; + return (0); + } else if (up.st_ino == caller->ino && up.st_dev == caller->dev) { + return (0); } - /* - * since we exit on error here, we're guaranteed to at least - * have info in the first dir_id struct, initdir - */ - if (stat(".", &buffer) == -1) { - (void) fprintf(stderr, - gettext("rm: cannot stat current directory: ")); - perror(""); - exit(2); - } - initdir.dev = buffer.st_dev; - initdir.inode = buffer.st_ino; - initdir.next = NULL; - - lastdir = &initdir; - /* - * Starting from current working directory, walk toward the - * root, looking at each directory along the way. - */ - for (;;) { - if (chdir("..") == -1 || lstat(".", &buffer) == -1) { - parent_err = 1; - break; - } - - if ((lastdir->next = malloc(sizeof (struct dir_id))) == - NULL) { + /* Directory hard link, check cycle */ + for (ptr = caller; ptr != NULL; ptr = ptr->up) { + if (frmp->dev == ptr->dev && frmp->ino == ptr->ino) { (void) fprintf(stderr, - gettext("rm: Insufficient memory.\n")); - cleanup(); - exit(1); + gettext("rm: cycle detected for %s\n"), pathbuf); + errcnt++; + return (-1); } - - curdir = lastdir->next; - curdir->dev = buffer.st_dev; - curdir->inode = buffer.st_ino; - curdir->next = NULL; - - /* - * Stop when we reach the root; note that chdir("..") - * at the root dir will stay in root. Get rid of - * the redundant dir_id struct for root. - */ - if (curdir->dev == lastdir->dev && curdir->inode == - lastdir->inode) { - lastdir->next = NULL; - free(curdir); - break; - } - - /* loop again to go back another level */ - lastdir = curdir; } - /* go back to init directory */ - chdir_init(); + caller->flags |= DIR_CANTCLOSE; + return (0); } - -/* - * cleanup the dynamically-allocated list of device numbers and inodes, - * if any. If initdir was never used, it is external and static so - * it is guaranteed initialized to zero, thus initdir.next would be NULL. - */ - -static void -cleanup(void) { - - struct dir_id *lastdir, *curdir; - - curdir = initdir.next; - - while (curdir != NULL) { - lastdir = curdir; - curdir = curdir->next; - free(lastdir); - } -}
--- a/usr/src/cmd/truss/systable.c Mon Mar 19 11:35:26 2007 -0700 +++ b/usr/src/cmd/truss/systable.c Mon Mar 19 14:20:27 2007 -0700 @@ -719,8 +719,9 @@ {"unlinkat", 4, DEC, NOV, HID, ATC, STG, HEX}, /* 5 */ {"futimesat", 4, DEC, NOV, HID, ATC, STG, HEX}, /* 6 */ {"renameat", 5, DEC, NOV, HID, ATC, STG, DEC, STG}, /* 7 */ -{"openat", 4, DEC, NOV, HID, ATC, STG, OPN}, /* 8 */ -{"openat64", 4, DEC, NOV, HID, ATC, STG, OPN}, /* 9 */ +{"__accessat", 5, DEC, NOV, HID, ATC, STG, ACC}, /* 8 */ +{"openat", 4, DEC, NOV, HID, ATC, STG, OPN}, /* N - 2 */ +{"openat64", 4, DEC, NOV, HID, ATC, STG, OPN}, /* N - 1 */ }; #define NFSATSYSCODE (sizeof (fsatsystable) / sizeof (struct systable)) @@ -944,6 +945,7 @@ { "unlinkat", SYS_fsat }, { "futimesat", SYS_fsat }, { "renameat", SYS_fsat }, + { "__accessat", SYS_fsat }, { "lgrpsys", SYS_lgrpsys }, { "getrusage", SYS_rusagesys }, { "getrusage_chld", SYS_rusagesys }, @@ -1217,13 +1219,13 @@ if (nsysarg > 3) subcode = (Lsp->pr_sysarg[3] & O_CREAT) ? - 0 : 8; + 0 : NFSATSYSCODE - 2; break; case 1: /* openat64 */ if (nsysarg > 3) subcode = (Lsp->pr_sysarg[3] & O_CREAT) ? - 1 : 9; + 1 : NFSATSYSCODE - 1; break; case 2: case 3: @@ -1231,6 +1233,7 @@ case 5: case 6: case 7: + case 8: subcode = arg0; } break;
--- a/usr/src/lib/libc/amd64/Makefile Mon Mar 19 11:35:26 2007 -0700 +++ b/usr/src/lib/libc/amd64/Makefile Mon Mar 19 14:20:27 2007 -0700 @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -785,6 +785,7 @@ execl.o \ execle.o \ execv.o \ + faccessat.o \ fcntl.o \ fsmisc.o \ fstatat.o \
--- a/usr/src/lib/libc/i386/Makefile.com Mon Mar 19 11:35:26 2007 -0700 +++ b/usr/src/lib/libc/i386/Makefile.com Mon Mar 19 14:20:27 2007 -0700 @@ -824,6 +824,7 @@ execl.o \ execle.o \ execv.o \ + faccessat.o \ fcntl.o \ fsmisc.o \ fstatat.o \
--- a/usr/src/lib/libc/port/llib-lc Mon Mar 19 11:35:26 2007 -0700 +++ b/usr/src/lib/libc/port/llib-lc Mon Mar 19 14:20:27 2007 -0700 @@ -1742,3 +1742,6 @@ /* label.c */ extern int is_system_labeled(void); + +/* until TOG resolves the name, keep this private */ +int __accessat(int, const char *, int);
--- a/usr/src/lib/libc/port/mapfile-vers Mon Mar 19 11:35:26 2007 -0700 +++ b/usr/src/lib/libc/port/mapfile-vers Mon Mar 19 14:20:27 2007 -0700 @@ -1262,6 +1262,7 @@ SUNWprivate_1.1 { global: + __accessat; _a64l; acctctl; _acctctl;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/lib/libc/port/sys/faccessat.c Mon Mar 19 14:20:27 2007 -0700 @@ -0,0 +1,36 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "synonyms.h" +#include <sys/types.h> +#include <sys/syscall.h> + +int +__accessat(int fd, const char *name, int amode) +{ + return (syscall(SYS_fsat, 8, fd, name, amode)); +}
--- a/usr/src/lib/libc/sparc/Makefile Mon Mar 19 11:35:26 2007 -0700 +++ b/usr/src/lib/libc/sparc/Makefile Mon Mar 19 14:20:27 2007 -0700 @@ -849,6 +849,7 @@ execl.o \ execle.o \ execv.o \ + faccessat.o \ fcntl.o \ fsmisc.o \ fstatat.o \
--- a/usr/src/lib/libc/sparcv9/Makefile Mon Mar 19 11:35:26 2007 -0700 +++ b/usr/src/lib/libc/sparcv9/Makefile Mon Mar 19 14:20:27 2007 -0700 @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -794,6 +794,7 @@ execl.o \ execle.o \ execv.o \ + faccessat.o \ fcntl.o \ fsmisc.o \ fstatat.o \
--- a/usr/src/uts/common/fs/vnode.c Mon Mar 19 11:35:26 2007 -0700 +++ b/usr/src/uts/common/fs/vnode.c Mon Mar 19 14:20:27 2007 -0700 @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -2511,6 +2511,9 @@ * a safe manner. If the vnode already has path information embedded, then the * cached path is left untouched. */ + +size_t max_vnode_path = 4 * MAXPATHLEN; + void vn_setpath(vnode_t *rootvp, struct vnode *startvp, struct vnode *vp, const char *path, size_t plen) @@ -2553,6 +2556,10 @@ */ mutex_exit(&base->v_lock); + /* Paths should stay within reason */ + if (rpathalloc > max_vnode_path) + return; + rpath = kmem_alloc(rpathalloc, KM_SLEEP); mutex_enter(&base->v_lock);
--- a/usr/src/uts/common/syscall/access.c Mon Mar 19 11:35:26 2007 -0700 +++ b/usr/src/uts/common/syscall/access.c Mon Mar 19 14:20:27 2007 -0700 @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -45,7 +45,9 @@ #include <sys/uio.h> #include <sys/cmn_err.h> #include <sys/debug.h> +#include <sys/file.h> #include <fs/fs_subr.h> +#include <c2/audit.h> /* * Determine accessibility of file. @@ -56,8 +58,8 @@ #define W_OK 002 #define X_OK 001 -int -access(char *fname, int fmode) +static int +caccess(char *fname, int fmode, vnode_t *startvp) { vnode_t *vp; cred_t *tmpcr; @@ -89,7 +91,8 @@ } lookup: - if (error = lookupname(fname, UIO_USERSPACE, FOLLOW, NULLVPP, &vp)) { + if (error = lookupnameat(fname, UIO_USERSPACE, FOLLOW, NULLVPP, &vp, + startvp)) { if ((error == ESTALE) && fs_need_estale_retry(estale_retry++)) goto lookup; if (!eok) @@ -114,3 +117,53 @@ VN_RELE(vp); return (error); } + +int +access(char *fname, int fmode) +{ + return (caccess(fname, fmode, NULL)); +} + +int +accessat(int fd, char *fname, int fmode) +{ + file_t *dirfp; + vnode_t *dirvp; + int error; + char startchar; + + if (fd == AT_FDCWD && fname == NULL) + return (set_errno(EFAULT)); + + if (fname != NULL) { + if (copyin(fname, &startchar, sizeof (char))) + return (set_errno(EFAULT)); + } else + startchar = '\0'; + + if (fd == AT_FDCWD) { + dirvp = NULL; + } else { + if (startchar != '/') { + if ((dirfp = getf(fd)) == NULL) { + return (set_errno(EBADF)); + } + dirvp = dirfp->f_vnode; + VN_HOLD(dirvp); + releasef(fd); + } else { + dirvp = NULL; + } + } + +#ifdef C2_AUDIT + if (audit_active) + audit_setfsat_path(1); +#endif /* C2_AUDIT */ + + error = caccess(fname, fmode, dirvp); + if (dirvp != NULL) + VN_RELE(dirvp); + + return (error); +}
--- a/usr/src/uts/common/syscall/fsat.c Mon Mar 19 11:35:26 2007 -0700 +++ b/usr/src/uts/common/syscall/fsat.c Mon Mar 19 14:20:27 2007 -0700 @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -42,6 +41,7 @@ extern int fchownat(int, char *, uid_t, gid_t, int); extern int fstatat(int, char *, struct stat *, int); extern int futimesat(int, char *, struct timeval *); +extern int accessat(int, char *, int); #if defined(_SYSCALL32_IMPL) || defined(_ILP32) extern int fstatat64_32(int, char *, struct stat64_32 *, int); extern int fstatat32(int, char *, struct stat32 *, int); @@ -64,6 +64,7 @@ * 5 - unlinkat * 6 - futimesat * 7 - renameat + * 8 - accessat * * The code for handling the at functionality exists in the file where the * base syscall is defined. For example openat is in open.c @@ -115,6 +116,8 @@ case 7: /* renameat */ return (renameat((int)arg1, (char *)arg2, (int)arg3, (char *)arg4)); + case 8: /* accessat */ + return (accessat((int)arg1, (char *)arg2, (int)arg3)); default: return (set_errno(EINVAL)); } @@ -155,6 +158,8 @@ case 7: /* renameat */ return (renameat((int)arg1, (char *)arg2, (int)arg3, (char *)arg4)); + case 8: /* accessat */ + return (accessat((int)arg1, (char *)arg2, (int)arg3)); default: return (set_errno(EINVAL)); }