(linenum→info "unix/slp.c:2238")

coreutils/6.9/lib/dirchownmod.c

    1: /* Change the ownership and mode bits of a directory.
    2: 
    3:    Copyright (C) 2006 Free Software Foundation, Inc.
    4: 
    5:    This program is free software; you can redistribute it and/or modify
    6:    it under the terms of the GNU General Public License as published by
    7:    the Free Software Foundation; either version 2, or (at your option)
    8:    any later version.
    9: 
   10:    This program is distributed in the hope that it will be useful,
   11:    but WITHOUT ANY WARRANTY; without even the implied warranty of
   12:    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13:    GNU General Public License for more details.
   14: 
   15:    You should have received a copy of the GNU General Public License
   16:    along with this program; if not, write to the Free Software Foundation,
   17:    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
   18: 
   19: /* Written by Paul Eggert.  */
   20: 
   21: #include <config.h>
   22: 
   23: #include "dirchownmod.h"
   24: 
   25: #include <errno.h>
   26: #include <sys/types.h>
   27: #include <sys/stat.h>
   28: #include <unistd.h>
   29: 
   30: #include "lchmod.h"
   31: #include "lchown.h"
   32: #include "stat-macros.h"
   33: 
   34: #ifndef HAVE_FCHMOD
   35: # define HAVE_FCHMOD 0
   36: # undef fchmod
   37: # define fchmod(fd, mode) (-1)
   38: #endif
   39: 
   40: /* Change the ownership and mode bits of a directory.  If FD is
   41:    nonnegative, it should be a file descriptor associated with the
   42:    directory; close it before returning.  DIR is the name of the
   43:    directory.
   44: 
   45:    If MKDIR_MODE is not (mode_t) -1, mkdir (DIR, MKDIR_MODE) has just
   46:    been executed successfully with umask zero, so DIR should be a
   47:    directory (not a symbolic link).
   48: 
   49:    First, set the file's owner to OWNER and group to GROUP, but leave
   50:    the owner alone if OWNER is (uid_t) -1, and similarly for GROUP.
   51: 
   52:    Then, set the file's mode bits to MODE, except preserve any of the
   53:    bits that correspond to zero bits in MODE_BITS.  In other words,
   54:    MODE_BITS is a mask that specifies which of the file's mode bits
   55:    should be set or cleared.  MODE should be a subset of MODE_BITS,
   56:    which in turn should be a subset of CHMOD_MODE_BITS.
   57: 
   58:    This implementation assumes the current umask is zero.
   59: 
   60:    Return 0 if successful, -1 (setting errno) otherwise.  Unsuccessful
   61:    calls may do the chown but not the chmod.  */
   62: 
   63: int
   64: dirchownmod (int fd, char const *dir, mode_t mkdir_mode,
   65:              uid_t owner, gid_t group,
   66:              mode_t mode, mode_t mode_bits)
   67: {
   68:   struct stat st;
   69:   int result = (fd < 0 ? stat (dir, &st) : fstat (fd, &st));
   70: 
   71:   if (result == 0)
   72:     {
   73:       mode_t dir_mode = st.st_mode;
   74: 
   75:       /* Check whether DIR is a directory.  If FD is nonnegative, this
   76:          check avoids changing the ownership and mode bits of the
   77:          wrong file in many cases.  This doesn't fix all the race
   78:          conditions, but it is better than nothing.  */
   79:       if (! S_ISDIR (dir_mode))
   80:         {
   81:           errno = ENOTDIR;
   82:           result = -1;
   83:         }
   84:       else
   85:         {
   86:           /* If at least one of the S_IXUGO bits are set, chown might
   87:              clear the S_ISUID and S_SGID bits.  Keep track of any
   88:              file mode bits whose values are indeterminate due to this
   89:              issue.  */
   90:           mode_t indeterminate = 0;
   91: 
   92:           /* On some systems, chown clears S_ISUID and S_ISGID, so do
   93:              chown before chmod.  On older System V hosts, ordinary
   94:              users can give their files away via chown; don't worry
   95:              about that here, since users shouldn't do that.  */
   96: 
   97:           if ((owner != (uid_t) -1 && owner != st.st_uid)
   98:               || (group != (gid_t) -1 && group != st.st_gid))
   99:             {
  100:               result = (0 <= fd
  101:                         ? fchown (fd, owner, group)
  102:                         : mkdir_mode != (mode_t) -1
  103:                         ? lchown (dir, owner, group)
  104:                         : chown (dir, owner, group));
  105: 
  106:               /* Either the user cares about an indeterminate bit and
  107:                  it'll be set properly by chmod below, or the user
  108:                  doesn't care and it's OK to use the bit's pre-chown
  109:                  value.  So there's no need to re-stat DIR here.  */
  110: 
  111:               if (result == 0 && (dir_mode & S_IXUGO))
  112:                 indeterminate = dir_mode & (S_ISUID | S_ISGID);
  113:             }
  114: 
  115:           /* If the file mode bits might not be right, use chmod to
  116:              change them.  Don't change bits the user doesn't care
  117:              about.  */
  118:           if (result == 0 && (((dir_mode ^ mode) | indeterminate) & mode_bits))
  119:             {
  120:               mode_t chmod_mode =
  121:                 mode | (dir_mode & CHMOD_MODE_BITS & ~mode_bits);
  122:               result = (HAVE_FCHMOD && 0 <= fd
  123:                         ? fchmod (fd, chmod_mode)
  124:                         : mkdir_mode != (mode_t) -1
  125:                         ? lchmod (dir, chmod_mode)
  126:                         : chmod (dir, chmod_mode));
  127:             }
  128:         }
  129:     }
  130: 
  131:   if (0 <= fd)
  132:     {
  133:       if (result == 0)
  134:         result = close (fd);
  135:       else
  136:         {
  137:           int e = errno;
  138:           close (fd);
  139:           errno = e;
  140:         }
  141:     }
  142: 
  143:   return result;
  144: }
Syntax (Markdown)