This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

euidaccessat [Re: one more openat-style function required: fchmodat


Roland McGrath <roland@redhat.com> wrote:
> It's a natural, obvious, and useful addition, no matter what Solaris does
> or doesn't have.  I've put it in.

Thanks for the speedy addition!

> If there are any more *at additions that
> might be useful, right now is the time to bring them up.  So rack your brain.

In case I didn't mention it on this list before, euidaccessat is one
function that is required to implement a POSIX-conforming rm(1) based
on a chdir-free recursive-removal function.

Here is the relevant function from coreutils/src/remove.c.
If you can see a way to do this without a function like euidaccessat,
POSIX-conformance weenies using rm on deep ACL/xattr-protected trees
will thank you.  FYI, this function serves only to determine whether
rm (without -f) should prompt the user before removing the file or
directory in question, so it's not a big deal either way.


/* Return true if FILE is determined to be an unwritable non-symlink.
   Otherwise, return false (including when lstat'ing it fails).
   If lstat (aka fstatat) succeeds, set *BUF_P to BUF.
   This is to avoid calling euidaccess when FILE is a symlink.  */
static bool
write_protected_non_symlink (int fd_cwd,
			     char const *file,
			     Dirstack_state const *ds,
			     struct stat **buf_p,
			     struct stat *buf)
{
  if (fstatat (fd_cwd, file, buf, AT_SYMLINK_NOFOLLOW) != 0)
    return false;
  *buf_p = buf;
  if (S_ISLNK (buf->st_mode))
    return false;
  /* Here, we know FILE is not a symbolic link.  */

  /* In order to be reentrant -- i.e., to avoid changing the working
     directory, and at the same time to be able to deal with alternate
     access control mechanisms (ACLs, xattr-style attributes) and
     arbitrarily deep trees -- we need a function like eaccessat, i.e.,
     like Solaris' eaccess, but fd-relative, in the spirit of openat.  */

  /* In the absence of a native eaccessat function, here are some of
     the implementation choices [#4 and #5 were suggested by Paul Eggert]:
     1) call openat with O_WRONLY|O_NOCTTY
	Disadvantage: may create the file and doesn't work for directory,
	may mistakenly report `unwritable' for EROFS or ACLs even though
	perm bits say the file is writable.

     2) fake eaccessat (save_cwd, fchdir, call euidaccess, restore_cwd)
	Disadvantage: changes working directory (not reentrant) and can't
	work if save_cwd fails.

     3) if (euidaccess (full_filename (file), W_OK) == 0)
	Disadvantage: doesn't work if full_filename is too long.
	Inefficient for very deep trees (O(Depth^2)).

     4) If the full pathname is sufficiently short (say, less than
	PATH_MAX or 8192 bytes, whichever is shorter):
	use method (3) (i.e., euidaccess (full_filename (file), W_OK));
	Otherwise: vfork, fchdir in the child, run euidaccess in the
	child, then the child exits with a status that tells the parent
	whether euidaccess succeeded.

	This avoids the O(N**2) algorithm of method (3), and it also avoids
	the failure-due-to-too-long-file-names of method (3), but it's fast
	in the normal shallow case.  It also avoids the lack-of-reentrancy
	and the save_cwd problems.
	Disadvantage; it uses a process slot for very-long file names,
	and would be very slow for hierarchies with many such files.

     5) If the full file name is sufficiently short (say, less than
	PATH_MAX or 8192 bytes, whichever is shorter):
	use method (3) (i.e., euidaccess (full_filename (file), W_OK));
	Otherwise: look just at the file bits.  Perhaps issue a warning
	the first time this occurs.

	This is like (4), except for the "Otherwise" case where it isn't as
	"perfect" as (4) but is considerably faster.  It conforms to current
	POSIX, and is uniformly better than what Solaris and FreeBSD do (they
	mess up with long file names). */

  {
    /* This implements #5: */
    size_t file_name_len
      = obstack_object_size (&ds->dir_stack) + strlen (file);

    return (file_name_len < MIN (PATH_MAX, 8192)
	    ? euidaccess (full_filename (file), W_OK) != 0 && errno == EACCES
	    : euidaccess_stat (buf, W_OK) != 0);
  }
}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]