summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/cmd/rm/Makefile3
-rw-r--r--usr/src/cmd/rm/rm.c1242
-rw-r--r--usr/src/cmd/truss/systable.c11
-rw-r--r--usr/src/lib/libc/amd64/Makefile3
-rw-r--r--usr/src/lib/libc/i386/Makefile.com1
-rw-r--r--usr/src/lib/libc/port/llib-lc3
-rw-r--r--usr/src/lib/libc/port/mapfile-vers1
-rw-r--r--usr/src/lib/libc/port/sys/faccessat.c36
-rw-r--r--usr/src/lib/libc/sparc/Makefile1
-rw-r--r--usr/src/lib/libc/sparcv9/Makefile3
-rw-r--r--usr/src/uts/common/fs/vnode.c9
-rw-r--r--usr/src/uts/common/syscall/access.c61
-rw-r--r--usr/src/uts/common/syscall/fsat.c13
13 files changed, 553 insertions, 834 deletions
diff --git a/usr/src/cmd/rm/Makefile b/usr/src/cmd/rm/Makefile
index 5bceb7cbbe..86ae34f79b 100644
--- a/usr/src/cmd/rm/Makefile
+++ b/usr/src/cmd/rm/Makefile
@@ -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 @@ include ../Makefile.cmd
CFLAGS += $(CCVERBOSE)
$(XPG4) := CFLAGS += -DXPG4
CPPFLAGS += -D_FILE_OFFSET_BITS=64
-LDLIBS += -lcmdutils
.KEEP_STATE:
diff --git a/usr/src/cmd/rm/rm.c b/usr/src/cmd/rm/rm.c
index 4686cfdb33..098a6680b3 100644
--- a/usr/src/cmd/rm/rm.c
+++ b/usr/src/cmd/rm/rm.c
@@ -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 struct stat rootdir;
-static char yeschr[SCHAR_MAX + 2];
-static char nochr[SCHAR_MAX + 2];
+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 */
+};
-static char *fullpath;
-static int initdirfd;
+static struct dlist top = {
+ (int)AT_FDCWD,
+ DIR_CANTCLOSE,
+};
-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 yeschr[SCHAR_MAX + 2];
+static char nochr[SCHAR_MAX + 2];
-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 */
+static struct dlist *cur, *rec;
-struct dir_id {
- dev_t dev;
- ino_t inode;
- struct dir_id *next;
-};
+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;
- /*
- * 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 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 @@ main(int argc, char *argv[])
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 @@ main(int argc, char *argv[])
argv = &argv[optind];
if ((argc < 1 && !silent) || errflg) {
- (void) fprintf(stderr,
- gettext("usage: rm [-fiRr] file ...\n"));
+ (void) fprintf(stderr, gettext("usage: rm [-fiRr] file ...\n"));
exit(2);
}
- if (getrlimit(RLIMIT_NOFILE, &rl)) {
- perror("getrlimit");
+ ontty = isatty(STDIN_FILENO) != 0;
+
+ if (recursive && stat("/", &rootdir) != 0) {
+ (void) fprintf(stderr,
+ gettext("rm: cannot stat root directory: %s\n"),
+ strerror(errno));
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 int
-rm(char *path, int first)
+static void
+pushfilename(const char *fname)
{
- struct stat buffer;
- char *filepath;
- char *p;
- char resolved_path[PATH_MAX];
+ char *p;
+ const char *q = fname;
- /*
- * Check file to see if it exists.
- */
- if (lstat(path, &buffer) == FAIL) {
- if (!silent) {
- perror(path);
- ++errcode;
+ 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;
}
- return (0);
+ *p++ = *q++;
}
+ *p = '\0';
+ cur->pathend = p - pathbuf;
+}
- /* 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);
+static void
+closeframe(struct dlist *frm)
+{
+ if (frm->dp != NULL) {
+ (void) closedir(frm->dp);
+ nfds--;
+ frm->dp = NULL;
+ frm->fd = -1;
}
+}
- /* 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);
+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
+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 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;
+
/*
- * If it's a directory, remove its contents.
+ * 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 (DIRECTORY) {
+ pushfilename(entry);
+
+ if (fstatat(caller->fd, entry, &temp, AT_SYMLINK_NOFOLLOW) != 0) {
+ if (!silent) {
+ (void) fprintf(stderr, "rm: %s: %s\n", pathbuf,
+ strerror(errno));
+ errcnt++;
+ }
+ return (0);
+ }
+
+ 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 (first_dir) {
- check_initdir();
- current_dir = NULL;
- first_dir = 0;
- }
-
- return (undir(path, first, buffer.st_dev, buffer.st_ino));
- }
-
- filepath = get_filename(path);
-
- /*
- * 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 (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);
}
- } else if (!silent) {
/*
- * 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.
- *
* 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)?
- * where XXX is the permission mode bits of the file in octal
- * and <filename> is the file to be removed
+ * rm: examine files in directory <directoryname> (y/n)?
+ * where <directoryname> is the directory 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 (interactive && !confirm(stderr,
+ gettext("rm: examine files in directory %s (%s/%s)? "),
+ pathbuf, 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.
- */
+ frame.dev = temp.st_dev;
+ frame.ino = temp.st_ino;
+ frame.flags = 0;
+ flag = AT_REMOVEDIR;
- if (unlink(path) == FAIL) {
- if (errno == ENOENT) {
- free(filepath);
+#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);
}
-#ifndef XPG4
- if (!silent || interactive) {
#endif
- (void) fprintf(stderr,
- gettext("rm: %s not removed: "), filepath);
- perror("");
-#ifndef XPG4
- }
-#endif
- ++errcode;
- }
+ if (opendirat(caller->fd, entry, &frame) == -1) {
+ err = errno;
- 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;
+ 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));
- push_name(path, first);
+/*
+ * 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);
+ }
- /*
- * 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.
+ * 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 (!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) {
+ if (fstat(frame.fd, &temp) != 0 ||
+ frame.ino != temp.st_ino ||
+ frame.dev != temp.st_dev) {
(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));
+ gettext("rm: %s: directory renamed\n"), pathbuf);
+ closeframe(&frame);
+ errcnt++;
+ return (0);
}
- }
-#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");
+ if (caller != &top) {
+ if (checkdir(caller, &frame) != 0) {
+ closeframe(&frame);
+ goto unlinkit;
+ }
}
- errcode++;
- return (pop_name(first));
- }
-
- /*
- * Open the directory for reading.
- */
- if ((name = opendir(path)) == NULL) {
- int saveerrno = errno;
+ pushdir(&frame);
/*
- * If interactive, ask for acknowledgement.
+ * rm() only returns -1 if popdir failed at some point;
+ * frame.dp is no longer reliable and we must drop out.
*/
- 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));
- }
+ 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);
+
/*
- * If the directory is empty, we may be able to
- * go ahead and remove it.
+ * 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 (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;
+ if (caller != &top) {
+ caller->flags &= ~DIR_CANTCLOSE;
+ entry = &pathbuf[caller->up->pathend + 1];
}
-
- /* 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;
+ } else {
+ flag = 0;
}
-
+unlinkit:
/*
- * Read every directory entry.
+ * If interactive, ask for acknowledgement.
*/
- while ((direct = readdir(name)) != NULL) {
- /*
- * Ignore "." and ".." entries.
- */
- if (strcmp(direct->d_name, ".") == 0 ||
- strcmp(direct->d_name, "..") == 0)
- continue;
+ if (interactive) {
+ if (!confirm(stderr, gettext("rm: remove %s (%s/%s)? "),
+ pathbuf, yeschr, nochr)) {
+ return (0);
+ }
+ } else if (!silent && flag == 0) {
/*
- * Try to remove the file.
+ * 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. 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)?
+ * where XXX is the permission mode bits of the file in octal
+ * and <filename> is the file to be removed
+ *
*/
- len = strlen(direct->d_name) + 1;
- if (chdir_failed) {
- len += strlen(path) + 2;
+ 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);
}
+ }
- newpath = malloc(len);
- if (newpath == NULL) {
- (void) fprintf(stderr,
- gettext("rm: Insufficient memory.\n"));
- cleanup();
- exit(1);
- }
+ if (unlinkat(caller->fd, entry, flag) != 0) {
+ err = errno;
+ if (err == ENOENT)
+ return (0);
- if (!chdir_failed) {
- (void) strcpy(newpath, direct->d_name);
+ 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 {
- (void) snprintf(newpath, len, "%s/%s",
- path, direct->d_name);
- }
-
+#ifndef XPG4
+ if (!silent || interactive) {
+#endif
- /*
- * 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);
+ gettext("rm: %s not removed: %s\n"),
+ pathbuf, strerror(err));
+#ifndef XPG4
}
- } 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);
+#endif
}
+ errcnt++;
}
-
- 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));
+ return (0);
}
-
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 @@ yes(void)
break;
}
if (i < SCHAR_MAX)
- ans[i] = b;
+ ans[i] = (char)b;
}
if (i >= SCHAR_MAX) {
i = SCHAR_MAX;
@@ -642,387 +583,54 @@ yes(void)
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)
-{
- 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;
-}
-
static int
-pop_name(int first)
+confirm(FILE *fp, const char *q, ...)
{
- int retval = 0;
- char *slash;
- struct stat buffer;
- struct dir_id *remove_dir;
+ va_list ap;
- 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);
- }
- }
- }
-
- /*
- * 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);
+ 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);
}
- 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();
-}
-
-/*
- * 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);
}
+ caller->flags |= DIR_CANTCLOSE;
+ return (0);
}
diff --git a/usr/src/cmd/truss/systable.c b/usr/src/cmd/truss/systable.c
index 61ff8cb7dc..a890431275 100644
--- a/usr/src/cmd/truss/systable.c
+++ b/usr/src/cmd/truss/systable.c
@@ -719,8 +719,9 @@ static const struct systable fsatsystable[] = {
{"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 @@ const struct sysalias sysalias[] = {
{ "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 @@ getsubcode(private_t *pri)
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 @@ getsubcode(private_t *pri)
case 5:
case 6:
case 7:
+ case 8:
subcode = arg0;
}
break;
diff --git a/usr/src/lib/libc/amd64/Makefile b/usr/src/lib/libc/amd64/Makefile
index 41d5c3004a..aa55f4f126 100644
--- a/usr/src/lib/libc/amd64/Makefile
+++ b/usr/src/lib/libc/amd64/Makefile
@@ -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 @@ PORTSYS= \
execl.o \
execle.o \
execv.o \
+ faccessat.o \
fcntl.o \
fsmisc.o \
fstatat.o \
diff --git a/usr/src/lib/libc/i386/Makefile.com b/usr/src/lib/libc/i386/Makefile.com
index 196c00b14e..624db0e061 100644
--- a/usr/src/lib/libc/i386/Makefile.com
+++ b/usr/src/lib/libc/i386/Makefile.com
@@ -824,6 +824,7 @@ PORTSYS= \
execl.o \
execle.o \
execv.o \
+ faccessat.o \
fcntl.o \
fsmisc.o \
fstatat.o \
diff --git a/usr/src/lib/libc/port/llib-lc b/usr/src/lib/libc/port/llib-lc
index cff6f24c5d..ae99d31f84 100644
--- a/usr/src/lib/libc/port/llib-lc
+++ b/usr/src/lib/libc/port/llib-lc
@@ -1742,3 +1742,6 @@ int _autofssys(enum autofssys_op, void *);
/* label.c */
extern int is_system_labeled(void);
+
+/* until TOG resolves the name, keep this private */
+int __accessat(int, const char *, int);
diff --git a/usr/src/lib/libc/port/mapfile-vers b/usr/src/lib/libc/port/mapfile-vers
index 8f23456786..0475f99826 100644
--- a/usr/src/lib/libc/port/mapfile-vers
+++ b/usr/src/lib/libc/port/mapfile-vers
@@ -1262,6 +1262,7 @@ SUNW_0.7 { # SunOS 5.3 (Solaris 2.3)
SUNWprivate_1.1 {
global:
+ __accessat;
_a64l;
acctctl;
_acctctl;
diff --git a/usr/src/lib/libc/port/sys/faccessat.c b/usr/src/lib/libc/port/sys/faccessat.c
new file mode 100644
index 0000000000..ee328b9e7b
--- /dev/null
+++ b/usr/src/lib/libc/port/sys/faccessat.c
@@ -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));
+}
diff --git a/usr/src/lib/libc/sparc/Makefile b/usr/src/lib/libc/sparc/Makefile
index b041d26ac4..019dd10a7e 100644
--- a/usr/src/lib/libc/sparc/Makefile
+++ b/usr/src/lib/libc/sparc/Makefile
@@ -849,6 +849,7 @@ PORTSYS= \
execl.o \
execle.o \
execv.o \
+ faccessat.o \
fcntl.o \
fsmisc.o \
fstatat.o \
diff --git a/usr/src/lib/libc/sparcv9/Makefile b/usr/src/lib/libc/sparcv9/Makefile
index 04054aaa81..77b46d2f4c 100644
--- a/usr/src/lib/libc/sparcv9/Makefile
+++ b/usr/src/lib/libc/sparcv9/Makefile
@@ -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 @@ PORTSYS= \
execl.o \
execle.o \
execv.o \
+ faccessat.o \
fcntl.o \
fsmisc.o \
fstatat.o \
diff --git a/usr/src/uts/common/fs/vnode.c b/usr/src/uts/common/fs/vnode.c
index 49bde7abeb..b18074e49d 100644
--- a/usr/src/uts/common/fs/vnode.c
+++ b/usr/src/uts/common/fs/vnode.c
@@ -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 @@ fs_new_caller_id()
* 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 @@ vn_setpath(vnode_t *rootvp, struct vnode *startvp, struct vnode *vp,
*/
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);
diff --git a/usr/src/uts/common/syscall/access.c b/usr/src/uts/common/syscall/access.c
index 89cb2373c7..0c19d8898a 100644
--- a/usr/src/uts/common/syscall/access.c
+++ b/usr/src/uts/common/syscall/access.c
@@ -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 @@ access(char *fname, int fmode)
}
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 @@ lookup:
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);
+}
diff --git a/usr/src/uts/common/syscall/fsat.c b/usr/src/uts/common/syscall/fsat.c
index 5e78a738c7..a558518d85 100644
--- a/usr/src/uts/common/syscall/fsat.c
+++ b/usr/src/uts/common/syscall/fsat.c
@@ -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 unlinkat(int, char *, int);
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 @@ extern int fstatat64_32(int, char *, struct stat64_32 *, int);
* 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 @@ fsat32(int code, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
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 @@ fsat64(int code, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
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));
}