843 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			843 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
| /* setfacl.c
 | |
| 
 | |
|    Written by Corinna Vinschen <vinschen@redhat.com>
 | |
| 
 | |
| This file is part of Cygwin.
 | |
| 
 | |
| This software is a copyrighted work licensed under the terms of the
 | |
| Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
 | |
| details. */
 | |
| 
 | |
| #include <errno.h>
 | |
| #include <stdio.h>
 | |
| #include <ctype.h>
 | |
| #include <string.h>
 | |
| #include <stdlib.h>
 | |
| #include <unistd.h>
 | |
| #include <getopt.h>
 | |
| #include <pwd.h>
 | |
| #include <grp.h>
 | |
| #include <cygwin/acl.h>
 | |
| #include <cygwin/version.h>
 | |
| 
 | |
| #ifndef BOOL
 | |
| #define BOOL int
 | |
| #endif
 | |
| 
 | |
| #ifndef TRUE
 | |
| #define TRUE (1)
 | |
| #endif
 | |
| 
 | |
| #ifndef FALSE
 | |
| #define FALSE (0)
 | |
| #endif
 | |
| 
 | |
| #ifndef ILLEGAL_MODE
 | |
| #define ILLEGAL_MODE ((mode_t)0xffffffff)
 | |
| #endif
 | |
| 
 | |
| static char *prog_name;
 | |
| 
 | |
| typedef enum {
 | |
|   NoAction = 0,
 | |
|   DeleteExt = 1,	/* The values 1,2,3 allow bitmasking below. */
 | |
|   DeleteDef = 2,
 | |
|   DeleteAll = 3,
 | |
|   Set,
 | |
|   Modify,
 | |
|   Delete,
 | |
|   ModNDel,
 | |
|   SetFromFile
 | |
| } action_t;
 | |
| 
 | |
| int mask_opt = 0;
 | |
| 
 | |
| mode_t getperm (char *in)
 | |
| {
 | |
|   if (isdigit ((unsigned char) *in) && !in[1])
 | |
|     {
 | |
|       int i = atoi (in);
 | |
|       if (i < 0 || i > 7)
 | |
| 	return ILLEGAL_MODE;
 | |
|       return i << 6 | i << 3 | i;
 | |
|     }
 | |
|   if (strlen (in) > 3 && strchr (" \t\n\r#", in[3]))
 | |
|     in[3] = '\0';
 | |
|   if (strlen (in) != 3)
 | |
|     return ILLEGAL_MODE;
 | |
|   if (!strchr ("r-", in[0])
 | |
|       || !strchr ("w-", in[1])
 | |
|       || !strchr ("x-", in[2]))
 | |
|     return ILLEGAL_MODE;
 | |
|   return (in[0] == 'r' ? S_IROTH : 0)
 | |
| 	 | (in[1] == 'w' ? S_IWOTH : 0)
 | |
| 	 | (in[2] == 'x' ? S_IXOTH : 0);
 | |
| }
 | |
| 
 | |
| BOOL
 | |
| getaclentry (action_t action, char *c, aclent_t *ace)
 | |
| {
 | |
|   char *c2;
 | |
| 
 | |
|   ace->a_type = 0;
 | |
|   ace->a_id = (uid_t) -1;
 | |
|   ace->a_perm = 0;
 | |
| 
 | |
|   /* First, check if we're handling a default entry. */
 | |
|   if (!strncmp (c, "default:", 8) || !strncmp (c, "d:", 2))
 | |
|     {
 | |
|       ace->a_type = ACL_DEFAULT;
 | |
|       c = strchr (c, ':') + 1;
 | |
|     }
 | |
|   /* c now points to the type.  Check for next colon.  If we find a colon,
 | |
|      NUL it.  Otherwise the string is invalid, except when deleting. */
 | |
|   c2 = strchrnul (c, ':');
 | |
|   if (*c2 == ':')
 | |
|     *c2++ = '\0';
 | |
|   else if (action != Delete)
 | |
|     return FALSE;
 | |
|   /* Fetch the type. */
 | |
|   if (!strcmp (c, "u") || !strcmp (c, "user"))
 | |
|       ace->a_type |= USER_OBJ;
 | |
|   else if (!strcmp (c, "g") || !strcmp (c, "group"))
 | |
|       ace->a_type |= GROUP_OBJ;
 | |
|   else if (!strcmp (c, "m") || !strcmp (c, "mask"))
 | |
|       ace->a_type |= CLASS_OBJ;
 | |
|   else if (!strcmp (c, "o") || !strcmp (c, "other"))
 | |
|       ace->a_type |= OTHER_OBJ;
 | |
|   else
 | |
|     return FALSE;
 | |
|   /* Skip to next field. */
 | |
|   c = c2;
 | |
|   if (!*c)
 | |
|     {
 | |
|       /* Nothing follows.  This is only valid if action is Delete and the
 | |
| 	 type is CLASS_OBJ, or if ACL_DEFAULT is set. */
 | |
|       if (action != Delete
 | |
| 	  || (!(ace->a_type & (CLASS_OBJ | ACL_DEFAULT))))
 | |
| 	return FALSE;
 | |
|     }
 | |
|   else if (!(ace->a_type & (USER_OBJ | GROUP_OBJ)))
 | |
|     {
 | |
|       /* Mask and other entries may contain an extra colon. */
 | |
|       if (*c == ':')
 | |
| 	++c;
 | |
|     }
 | |
|   /* If this is a user or group entry, check if next char is a colon char.
 | |
|      If so, skip it, otherwise it's the name of a user or group. */
 | |
|   else if (*c == ':')
 | |
|     ++c;
 | |
|   else if (*c)
 | |
|     {
 | |
|       /* c now points to the id.  Check for next colon.  If we find a colon,
 | |
| 	 NUL it.  Otherwise the string is invalid, except when deleting.
 | |
| 	 If we delete, it must be a default entry since standard ugo entries
 | |
| 	 can't be deleted. */
 | |
|       c2 = strchrnul (c + 1, ':');
 | |
|       if (*c2 == ':')
 | |
| 	*c2++ = '\0';
 | |
|       else if (action != Delete)
 | |
| 	return FALSE;
 | |
|       /* Fetch user/group id. */
 | |
|       if (isdigit ((unsigned char) *c))
 | |
| 	{
 | |
| 	  char *c3;
 | |
| 
 | |
| 	  ace->a_id = strtol (c, &c3, 10);
 | |
| 	  if (*c3)
 | |
| 	    return FALSE;
 | |
| 	}
 | |
|       else if (ace->a_type & USER_OBJ)
 | |
| 	{
 | |
| 	  struct passwd *pw = getpwnam (c);
 | |
| 	  if (!pw)
 | |
| 	    return FALSE;
 | |
| 	  ace->a_id = pw->pw_uid;
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  struct group *gr = getgrnam (c);
 | |
| 	  if (!gr)
 | |
| 	    return FALSE;
 | |
| 	  ace->a_id = gr->gr_gid;
 | |
| 	}
 | |
|       if (ace->a_type & USER_OBJ)
 | |
| 	{
 | |
| 	  ace->a_type &= ~USER_OBJ;
 | |
| 	  ace->a_type |= USER;
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  ace->a_type &= ~GROUP_OBJ;
 | |
| 	  ace->a_type |= GROUP;
 | |
| 	}
 | |
|       /* Skip to next field. */
 | |
|       c = c2;
 | |
|     }
 | |
|   if (action == Delete)
 | |
|     {
 | |
|       /* Trailing garbage? */
 | |
|       if (*c)
 | |
| 	return FALSE;
 | |
|       /* No, we're good. */
 | |
|       ace->a_perm = ILLEGAL_MODE;
 | |
|       return TRUE;
 | |
|     }
 | |
|   /* Check perms. */
 | |
|   if ((ace->a_perm = getperm (c)) == ILLEGAL_MODE)
 | |
|     return FALSE;
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| BOOL
 | |
| getaclentries (action_t action, char *buf, aclent_t *acls, int *idx)
 | |
| {
 | |
|   char *c;
 | |
| 
 | |
|   if (action == SetFromFile)
 | |
|     {
 | |
|       FILE *fp;
 | |
|       char fbuf[256], *fb;
 | |
| 
 | |
|       if (!strcmp (buf, "-"))
 | |
| 	fp = stdin;
 | |
|       else if (! (fp = fopen (buf, "r")))
 | |
| 	return FALSE;
 | |
|       while ((fb = fgets (fbuf, 256, fp)))
 | |
| 	{
 | |
| 	  while (strchr (" \t", *fb))
 | |
| 	    ++fb;
 | |
| 	  if (strchr ("\n\r#", *fb))
 | |
| 	    continue;
 | |
| 	  if (!getaclentry (action, fb, acls + (*idx)++))
 | |
| 	    {
 | |
| 	      fclose (fp);
 | |
| 	      return FALSE;
 | |
| 	    }
 | |
| 	}
 | |
|       if (fp != stdin)
 | |
| 	fclose (fp);
 | |
|     }
 | |
|   else
 | |
|     for (c = strtok (buf, ","); c; c = strtok (NULL, ","))
 | |
|       if (!getaclentry (action, c, acls + (*idx)++))
 | |
| 	return FALSE;
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| int
 | |
| searchace (aclent_t *aclp, int nentries, int type, int id)
 | |
| {
 | |
|   int i;
 | |
| 
 | |
|   for (i = 0; i < nentries; ++i)
 | |
|     if ((aclp[i].a_type == type && (id < 0 || aclp[i].a_id == id))
 | |
| 	|| !aclp[i].a_type)
 | |
|       return i;
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| int
 | |
| delace (aclent_t *tgt, int tcnt, int t)
 | |
| {
 | |
|   int i;
 | |
| 
 | |
|   for (i = t + 1; i < tcnt; ++i)
 | |
|     tgt[i - 1] = tgt[i];
 | |
|   --tcnt;
 | |
|   return tcnt;
 | |
| }
 | |
| 
 | |
| int
 | |
| delacl (aclent_t *tgt, int tcnt, aclent_t *src, int scnt)
 | |
| {
 | |
|   int t, s;
 | |
| 
 | |
|   for (s = 0; s < scnt; ++s)
 | |
|     {
 | |
|       t = searchace (tgt, MAX_ACL_ENTRIES, src[s].a_type,
 | |
| 		     (src[s].a_type & (USER | GROUP)) ? src[s].a_id : -1);
 | |
|       if (t < 0)
 | |
| 	return -1;
 | |
|       if (t < tcnt)
 | |
| 	tcnt = delace (tgt, tcnt, t);
 | |
|     }
 | |
|   return tcnt;
 | |
| }
 | |
| 
 | |
| int
 | |
| modacl (aclent_t *tgt, int tcnt, aclent_t *src, int scnt)
 | |
| {
 | |
|   int t, s;
 | |
| 
 | |
|   /* Delete, replace or add given acl entries. */
 | |
|   for (s = 0; s < scnt; ++s)
 | |
|     {
 | |
|       t = searchace (tgt, MAX_ACL_ENTRIES, src[s].a_type,
 | |
| 		     (src[s].a_type & (USER | GROUP)) ? src[s].a_id : -1);
 | |
|       if (t < 0)
 | |
| 	return -1;
 | |
|       /* ILLEGAL_MODE means "delete". */
 | |
|       if (src[s].a_perm == ILLEGAL_MODE && t < tcnt)
 | |
| 	tcnt = delace (tgt, tcnt, t);
 | |
|       else
 | |
| 	{
 | |
| 	  tgt[t] = src[s];
 | |
| 	  if (t >= tcnt)
 | |
| 	    ++tcnt;
 | |
| 	}
 | |
|     }
 | |
|   return tcnt;
 | |
| }
 | |
| 
 | |
| void
 | |
| check_got_mask (aclent_t *src, int scnt, int *got_mask, int *got_def_mask)
 | |
| {
 | |
|   *got_mask = searchace (src, scnt, CLASS_OBJ, -1) >= 0;
 | |
|   *got_def_mask = searchace (src, scnt, DEF_CLASS_OBJ, -1) >= 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| recompute_mask (aclent_t *tgt, int tcnt, int got_mask, int got_def_mask)
 | |
| {
 | |
|   int t;
 | |
|   int need_mask = 0, need_def_mask = 0;
 | |
|   int mask_idx = -1, def_mask_idx = -1;
 | |
|   mode_t mask = 0, def_mask = 0;
 | |
| 
 | |
|   /* Now recompute mask, if requested (default) */
 | |
|   for (t = 0; t < tcnt; ++t)
 | |
|     {
 | |
|       switch (tgt[t].a_type)
 | |
| 	{
 | |
| 	case USER:
 | |
| 	case GROUP:
 | |
| 	  /* Do we need a CLASS_OBJ at all? */
 | |
| 	  need_mask = 1;
 | |
| 	  /*FALLTHRU*/
 | |
| 	case GROUP_OBJ:
 | |
| 	  /* Compute resulting maximum mask. */
 | |
| 	  mask |= tgt[t].a_perm;
 | |
| 	  break;
 | |
| 	case CLASS_OBJ:
 | |
| 	  /* Do we already have a CLASS_OBJ? */
 | |
| 	  mask_idx = t;
 | |
| 	  break;
 | |
| 	case DEF_USER:
 | |
| 	case DEF_GROUP:
 | |
| 	  /* Do we need a DEF_CLASS_OBJ at all? */
 | |
| 	  need_def_mask = 1;
 | |
| 	  /*FALLTHRU*/
 | |
| 	case DEF_GROUP_OBJ:
 | |
| 	  /* Compute resulting maximum default mask. */
 | |
| 	  def_mask |= tgt[t].a_perm;
 | |
| 	  break;
 | |
| 	case DEF_CLASS_OBJ:
 | |
| 	  /* Do we already have a DEF_CLASS_OBJ? */
 | |
| 	  def_mask_idx = t;
 | |
| 	  break;
 | |
| 	}
 | |
|     }
 | |
|   /* Recompute mask, if requested
 | |
|      - If we got a mask in the input string, recompute only if --mask has been
 | |
|        specified.
 | |
|      - If we got no mask in the input, but we either need a mask or we already
 | |
|        have one, and --no-mask has *not* been specified, recompute. */
 | |
|   if ((got_mask && mask_opt > 0)
 | |
|       || (!got_mask && mask_opt >= 0 && (need_mask || mask_idx >= 0)))
 | |
|     {
 | |
|       if (mask_idx >= 0)
 | |
| 	t = mask_idx;
 | |
|       else
 | |
| 	t = searchace (tgt, MAX_ACL_ENTRIES, CLASS_OBJ, -1);
 | |
|       if (t < 0)
 | |
| 	return -1;
 | |
|       if (t >= tcnt)
 | |
| 	++tcnt;
 | |
|       tgt[t].a_type = CLASS_OBJ;
 | |
|       tgt[t].a_id = -1;
 | |
|       tgt[t].a_perm = mask;
 | |
|     }
 | |
|   /* Recompute default mask, if requested */
 | |
|   if ((got_def_mask && mask_opt > 0)
 | |
|       || (!got_def_mask && mask_opt >= 0
 | |
| 	  && (need_def_mask || def_mask_idx >= 0)))
 | |
|     {
 | |
|       if (def_mask_idx >= 0)
 | |
| 	t = def_mask_idx;
 | |
|       else
 | |
| 	t = searchace (tgt, MAX_ACL_ENTRIES, DEF_CLASS_OBJ, -1);
 | |
|       if (t < 0)
 | |
| 	return -1;
 | |
|       if (t >= tcnt)
 | |
| 	++tcnt;
 | |
|       tgt[t].a_type = DEF_CLASS_OBJ;
 | |
|       tgt[t].a_id = -1;
 | |
|       tgt[t].a_perm = def_mask;
 | |
|     }
 | |
|   return tcnt;
 | |
| }
 | |
| 
 | |
| int
 | |
| addmissing (aclent_t *tgt, int tcnt)
 | |
| {
 | |
|   int t;
 | |
|   int types = 0, def_types = 0;
 | |
|   int perm = 0, def_perm = 0;
 | |
| 
 | |
|   /* Check if we have all the required entries now. */
 | |
|   for (t = 0; t < tcnt; ++t)
 | |
|     if (tgt[t].a_type & ACL_DEFAULT)
 | |
|       {
 | |
| 	def_types |= tgt[t].a_type;
 | |
| 	if (tgt[t].a_type & GROUP_OBJ)
 | |
| 	  def_perm |= tgt[t].a_perm;
 | |
| 	else if ((tgt[t].a_type & (USER | GROUP)) && mask_opt >= 0)
 | |
| 	  def_perm |= tgt[t].a_perm;
 | |
|       }
 | |
|     else
 | |
|       {
 | |
| 	types |= tgt[t].a_type;
 | |
| 	if (tgt[t].a_type & GROUP_OBJ)
 | |
| 	  perm |= tgt[t].a_perm;
 | |
| 	else if ((tgt[t].a_type & (USER | GROUP)) && mask_opt >= 0)
 | |
| 	  perm |= tgt[t].a_perm;
 | |
|       }
 | |
|   /* Add missing CLASS_OBJ */
 | |
|   if ((types & (USER | GROUP)) && !(types & CLASS_OBJ))
 | |
|     {
 | |
|       tgt[tcnt].a_type = CLASS_OBJ;
 | |
|       tgt[tcnt].a_id = (uid_t) -1;
 | |
|       tgt[tcnt++].a_perm = perm;
 | |
|     }
 | |
|   if (def_types)
 | |
|     {
 | |
|       /* Add missing default entries. */
 | |
|       if (!(def_types & USER_OBJ) && tcnt < MAX_ACL_ENTRIES)
 | |
| 	{
 | |
| 	  t = searchace (tgt, tcnt, USER_OBJ, -1);
 | |
| 	  tgt[tcnt].a_type = DEF_USER_OBJ;
 | |
| 	  tgt[tcnt].a_id = (uid_t) -1;
 | |
| 	  tgt[tcnt++].a_perm = t >= 0 ? tgt[t].a_perm : S_IRWXO;
 | |
| 	}
 | |
|       if (!(def_types & GROUP_OBJ) && tcnt < MAX_ACL_ENTRIES)
 | |
| 	{
 | |
| 	  t = searchace (tgt, tcnt, GROUP_OBJ, -1);
 | |
| 	  tgt[tcnt].a_type = DEF_GROUP_OBJ;
 | |
| 	  tgt[tcnt].a_id = (uid_t) -1;
 | |
| 	  tgt[tcnt].a_perm = t >= 0 ? tgt[t].a_perm : (S_IROTH | S_IXOTH);
 | |
| 	  def_perm |= tgt[tcnt++].a_perm;
 | |
| 	}
 | |
|       if (!(def_types & OTHER_OBJ) && tcnt < MAX_ACL_ENTRIES)
 | |
| 	{
 | |
| 	  t = searchace (tgt, tcnt, OTHER_OBJ, -1);
 | |
| 	  tgt[tcnt].a_type = DEF_OTHER_OBJ;
 | |
| 	  tgt[tcnt].a_id = (uid_t) -1;
 | |
| 	  tgt[tcnt++].a_perm = t >= 0 ? tgt[t].a_perm : (S_IROTH | S_IXOTH);
 | |
| 	}
 | |
|       /* Add missing DEF_CLASS_OBJ */
 | |
|       if ((def_types & (USER | GROUP)) && !(def_types & CLASS_OBJ))
 | |
| 	{
 | |
| 	  tgt[tcnt].a_type = DEF_CLASS_OBJ;
 | |
| 	  tgt[tcnt].a_id = (uid_t) -1;
 | |
| 	  tgt[tcnt++].a_perm = def_perm;
 | |
| 	}
 | |
|     }
 | |
|   return tcnt;
 | |
| }
 | |
| 
 | |
| int
 | |
| delallacl (aclent_t *tgt, int tcnt, action_t action)
 | |
| {
 | |
|   int t;
 | |
| 
 | |
|   for (t = 0; t < tcnt; ++t)
 | |
|     /* -b (DeleteExt):    Remove all extended ACL entries.
 | |
|        -k (DeleteDef):    Remove all default ACL entries.
 | |
|        -b -k (DeleteAll): Remove extended and remove defaults.  That means,
 | |
| 			  only preserve standard POSIX perms. */
 | |
|     if (((action & DeleteExt) && (tgt[t].a_type & (USER | GROUP | CLASS_OBJ)))
 | |
| 	|| ((action & DeleteDef) && (tgt[t].a_type & ACL_DEFAULT)))
 | |
|       {
 | |
| 	--tcnt;
 | |
| 	if (t < tcnt)
 | |
| 	  memmove (&tgt[t], &tgt[t + 1], (tcnt - t) * sizeof (aclent_t));
 | |
| 	--t;
 | |
|       }
 | |
|   return tcnt;
 | |
| }
 | |
| 
 | |
| int
 | |
| setfacl (action_t action, const char *path, aclent_t *acls, int cnt)
 | |
| {
 | |
|   aclent_t lacl[MAX_ACL_ENTRIES];
 | |
|   int lcnt, got_mask = 0, got_def_mask = 0;
 | |
| 
 | |
|   memset (lacl, 0, sizeof lacl);
 | |
|   switch (action)
 | |
|     {
 | |
|     case Set:
 | |
|       check_got_mask (acls, cnt, &got_mask, &got_def_mask);
 | |
|       memcpy (lacl, acls, (lcnt = cnt) * sizeof (aclent_t));
 | |
|       if ((lcnt = recompute_mask (lacl, lcnt, got_mask, got_def_mask)) < 0
 | |
| 	  || (lcnt = addmissing (lacl, lcnt)) < 0
 | |
| 	  || acl (path, SETACL, lcnt, lacl) < 0)
 | |
| 	{
 | |
| 	  perror (prog_name);
 | |
| 	  return 2;
 | |
| 	}
 | |
|       break;
 | |
|     case Delete:
 | |
|       check_got_mask (acls, cnt, &got_mask, &got_def_mask);
 | |
|       if ((lcnt = acl (path, GETACL, MAX_ACL_ENTRIES, lacl)) < 0
 | |
| 	  || (lcnt = delacl (lacl, lcnt, acls, cnt)) < 0
 | |
| 	  || (lcnt = recompute_mask (lacl, lcnt, got_mask, got_def_mask)) < 0
 | |
| 	  || acl (path, SETACL, lcnt, lacl) < 0)
 | |
| 	{
 | |
| 	  perror (prog_name);
 | |
| 	  return 2;
 | |
| 	}
 | |
|       break;
 | |
|     case DeleteExt:
 | |
|     case DeleteDef:
 | |
|     case DeleteAll:
 | |
|       if ((lcnt = acl (path, GETACL, MAX_ACL_ENTRIES, lacl)) < 0
 | |
| 	  || (lcnt = delallacl (lacl, lcnt, action)) < 0
 | |
| 	  || acl (path, SETACL, lcnt, lacl) < 0)
 | |
| 	{
 | |
| 	  perror (prog_name);
 | |
| 	  return 2;
 | |
| 	}
 | |
|       break;
 | |
|     default:
 | |
|       check_got_mask (acls, cnt, &got_mask, &got_def_mask);
 | |
|       if ((lcnt = acl (path, GETACL, MAX_ACL_ENTRIES, lacl)) < 0
 | |
| 	  || (lcnt = modacl (lacl, lcnt, acls, cnt)) < 0
 | |
| 	  || (lcnt = recompute_mask (lacl, lcnt, got_mask, got_def_mask)) < 0
 | |
| 	  || (lcnt = addmissing (lacl, lcnt)) < 0
 | |
| 	  || acl (path, SETACL, lcnt, lacl) < 0)
 | |
| 	{
 | |
| 	  perror (prog_name);
 | |
| 	  return 2;
 | |
| 	}
 | |
|       break;
 | |
|     }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| static void
 | |
| usage (FILE *stream)
 | |
| {
 | |
|   fprintf (stream, ""
 | |
| "Usage: %s [-n] {-f ACL_FILE | -s acl_entries} FILE...\n"
 | |
| "       %s [-n] {[-bk]|[-x acl_entries] [-m acl_entries]} FILE...\n"
 | |
| "\n"
 | |
| "Modify file and directory access control lists (ACLs)\n"
 | |
| "\n"
 | |
| "  -b, --remove-all       remove all extended ACL entries\n"
 | |
| "  -x, --delete           delete one or more specified ACL entries\n"
 | |
| "  -f, --file             set ACL entries for FILE to ACL entries read\n"
 | |
| "                         from ACL_FILE\n"
 | |
| "  -k, --remove-default   remove all default ACL entries\n"
 | |
| "  -m, --modify           modify one or more specified ACL entries\n"
 | |
| "  -n, --no-mask          don't recalculate the effective rights mask\n"
 | |
| "      --mask             do recalculate the effective rights mask\n"
 | |
| "  -s, --set              set specified ACL entries on FILE\n"
 | |
| "  -V, --version          print version and exit\n"
 | |
| "  -h, --help             this help text\n"
 | |
| "\n"
 | |
| "At least one of (-b, -x, -f, -k, -m, -s) must be specified\n"
 | |
| "\n", prog_name, prog_name);
 | |
|     if (stream == stdout)
 | |
|     {
 | |
|       printf(""
 | |
| "  Acl_entries are one or more comma-separated ACL entries from the following\n"
 | |
| "  list:\n"
 | |
| "\n"
 | |
| "    u[ser]::perm\n"
 | |
| "    u[ser]:uid:perm\n"
 | |
| "    g[roup]::perm\n"
 | |
| "    g[roup]:gid:perm\n"
 | |
| "    m[ask]:perm\n"
 | |
| "    o[ther]:perm\n"
 | |
| "\n"
 | |
| "  Default entries are like the above with the additional default identifier.\n"
 | |
| "  For example: \n"
 | |
| "\n"
 | |
| "    d[efault]:u[ser]:uid:perm\n"
 | |
| "\n"
 | |
| "  'perm' is either a 3-char permissions string in the form \"rwx\" with the\n"
 | |
| "  character - for no permission, or it is the octal representation of the\n"
 | |
| "  permissions, a value from 0 (equivalent to \"---\") to 7 (\"rwx\").\n"
 | |
| "  'uid' is a user name or a numerical uid.\n"
 | |
| "  'gid' is a group name or a numerical gid.\n"
 | |
| "\n"
 | |
| "For each file given as parameter, %s will either replace its complete ACL\n"
 | |
| "(-s, -f), or it will add, modify, or delete ACL entries.\n"
 | |
| "\n"
 | |
| "The following options are supported:\n"
 | |
| "\n"
 | |
| "-b, --remove-all\n"
 | |
| "  Remove all extended ACL entries.  The base ACL entries of the owner, group\n"
 | |
| "  and others are retained.  This option can be combined with the\n"
 | |
| "  -k,--remove-default option to delete all non-standard POSIX permissions.\n"
 | |
| "\n"
 | |
| "-x, --delete\n"
 | |
| "  Delete one or more specified entries from the file's ACL.  The owner, group\n"
 | |
| "  and others entries must not be deleted.  Acl_entries to be deleted should\n"
 | |
| "  be specified without permissions, as in the following list:\n"
 | |
| "\n"
 | |
| "    u[ser]:uid[:]\n"
 | |
| "    g[roup]:gid[:]\n"
 | |
| "    m[ask][:]\n"
 | |
| "    d[efault]:u[ser][:uid]\n"
 | |
| "    d[efault]:g[roup][:gid]\n"
 | |
| "    d[efault]:m[ask][:]\n"
 | |
| "    d[efault]:o[ther][:]\n"
 | |
| "\n"
 | |
| "-f, --file\n"
 | |
| "  Take the Acl_entries from ACL_FILE one per line.  Whitespace characters are\n"
 | |
| "  ignored, and the character \"#\" may be used to start a comment.  The special\n"
 | |
| "  filename \"-\" indicates reading from stdin.\n"
 | |
| "  Required entries are\n"
 | |
| "  - One user entry for the owner of the file.\n"
 | |
| "  - One group entry for the group of the file.\n"
 | |
| "  - One other entry.\n"
 | |
| "  If additional user and group entries are given:\n"
 | |
| "  - A mask entry for the file group class of the file.\n"
 | |
| "  - No duplicate user or group entries with the same uid/gid.\n"
 | |
| "  If it is a directory:\n"
 | |
| "  - One default user entry for the owner of the file.\n"
 | |
| "  - One default group entry for the group of the file.\n"
 | |
| "  - One default mask entry for the file group class.\n"
 | |
| "  - One default other entry.\n"
 | |
| "\n"
 | |
| "-k, --remove-default\n"
 | |
| "  Remove all default ACL entries. If no default ACL entries exist, no\n"
 | |
| "  warnings are issued.  This option can be combined with the -b,--remove-all\n"
 | |
| "  option to delete all non-standard POSIX permissions.\n"
 | |
| "\n"
 | |
| "-m, --modify\n"
 | |
| "  Add or modify one or more specified ACL entries.  Acl_entries is a\n"
 | |
| "  comma-separated list of entries from the same list as above.\n"
 | |
| "\n"
 | |
| "-n, --no-mask\n"
 | |
| "  Valid in conjunction with -m.  Do not recalculate the effective rights\n"
 | |
| "  mask. The default behavior of setfacl is to recalculate the ACL mask entry,\n"
 | |
| "  unless a mask entry was explicitly given.  The mask entry is set to the\n"
 | |
| "  union of all permissions of the owning group, and all named user and group\n"
 | |
| "  entries.  (These are exactly the entries affected by the mask entry).\n"
 | |
| "\n"
 | |
| "--mask\n"
 | |
| "  Valid in conjunction with -m.  Do recalculate the effective rights mask,\n"
 | |
| "  even if an ACL mask entry was explicitly given. (See the -n option.)\n"
 | |
| "\n"
 | |
| "-s, --set\n"
 | |
| "  Like -f, but set the file's ACL with ACL entries specified in a\n"
 | |
| "  comma-separated list on the command line.\n"
 | |
| "\n"
 | |
| "While the -x and -m options may be used in the same command, the -f and -s\n"
 | |
| "options may be used only exclusively.\n"
 | |
| "\n"
 | |
| "Directories may contain default ACL entries.  Files created in a directory\n"
 | |
| "that contains default ACL entries will have permissions according to the\n"
 | |
| "combination of the current umask, the explicit permissions requested and\n"
 | |
| "the default ACL entries.\n"
 | |
| "\n", prog_name);
 | |
|   }
 | |
|   else
 | |
|     fprintf(stream, "Try '%s --help' for more information.\n", prog_name);
 | |
| }
 | |
| 
 | |
| struct option longopts[] = {
 | |
|   {"remove-all", no_argument, NULL, 'b'},
 | |
|   {"delete", required_argument, NULL, 'x'},
 | |
|   {"file", required_argument, NULL, 'f'},
 | |
|   {"remove-default", no_argument, NULL, 'k'},
 | |
|   {"modify", required_argument, NULL, 'm'},
 | |
|   {"no-mask", no_argument, NULL, 'n'},
 | |
|   {"mask", no_argument, NULL, '\n'},
 | |
|   {"replace", no_argument, NULL, 'r'},
 | |
|   {"set", required_argument, NULL, 's'},
 | |
|   {"substitute", required_argument, NULL, 's'},
 | |
|   {"help", no_argument, NULL, 'h'},
 | |
|   {"version", no_argument, NULL, 'V'},
 | |
|   {0, no_argument, NULL, 0}
 | |
| };
 | |
| const char *opts = "bd:f:hkm:nrs:Vx:";
 | |
| 
 | |
| static void
 | |
| print_version ()
 | |
| {
 | |
|   printf ("setfacl (cygwin) %d.%d.%d\n"
 | |
| 	  "POSIX ACL modification utility\n"
 | |
| 	  "Copyright (C) 2000 - %s Cygwin Authors\n"
 | |
| 	  "This is free software; see the source for copying conditions.  There is NO\n"
 | |
| 	  "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
 | |
| 	  CYGWIN_VERSION_DLL_MAJOR / 1000,
 | |
| 	  CYGWIN_VERSION_DLL_MAJOR % 1000,
 | |
| 	  CYGWIN_VERSION_DLL_MINOR,
 | |
| 	  strrchr (__DATE__, ' ') + 1);
 | |
| }
 | |
| 
 | |
| int
 | |
| main (int argc, char **argv)
 | |
| {
 | |
|   int c;
 | |
|   action_t action = NoAction;
 | |
|   aclent_t acls[MAX_ACL_ENTRIES];
 | |
|   int aclidx = 0;
 | |
|   int ret = 0;
 | |
| 
 | |
|   prog_name = program_invocation_short_name;
 | |
| 
 | |
|   memset (acls, 0, sizeof acls);
 | |
|   while ((c = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
 | |
|     switch (c)
 | |
|       {
 | |
|       case 'b':
 | |
| 	if (action == NoAction)
 | |
| 	  action = DeleteExt;
 | |
| 	else if (action == DeleteDef)
 | |
| 	  action = DeleteAll;
 | |
| 	else
 | |
| 	  {
 | |
| 	    usage (stderr);
 | |
| 	    return 1;
 | |
| 	  }
 | |
| 	break;
 | |
|       case 'd':		/* Backward compat */
 | |
|       case 'x':
 | |
| 	if (action == NoAction)
 | |
| 	  action = Delete;
 | |
| 	else if (action == Modify)
 | |
| 	  action = ModNDel;
 | |
| 	else
 | |
| 	  {
 | |
| 	    usage (stderr);
 | |
| 	    return 1;
 | |
| 	  }
 | |
| 	if (! getaclentries (Delete, optarg, acls, &aclidx))
 | |
| 	  {
 | |
| 	    fprintf (stderr, "%s: illegal acl entries\n", prog_name);
 | |
| 	    return 2;
 | |
| 	  }
 | |
| 	break;
 | |
|       case 'f':
 | |
| 	if (action == NoAction)
 | |
| 	  action = Set;
 | |
| 	else
 | |
| 	  {
 | |
| 	    usage (stderr);
 | |
| 	    return 1;
 | |
| 	  }
 | |
| 	if (! getaclentries (SetFromFile, optarg, acls, &aclidx))
 | |
| 	  {
 | |
| 	    fprintf (stderr, "%s: illegal acl entries\n", prog_name);
 | |
| 	    return 2;
 | |
| 	  }
 | |
| 	break;
 | |
|       case 'h':
 | |
| 	usage (stdout);
 | |
| 	return 0;
 | |
|       case 'k':
 | |
| 	if (action == NoAction)
 | |
| 	  action = DeleteDef;
 | |
| 	else if (action == DeleteExt)
 | |
| 	  action = DeleteAll;
 | |
| 	else
 | |
| 	  {
 | |
| 	    usage (stderr);
 | |
| 	    return 1;
 | |
| 	  }
 | |
| 	break;
 | |
|       case 'm':
 | |
| 	if (action == NoAction)
 | |
| 	  action = Modify;
 | |
| 	else if (action == Delete)
 | |
| 	  action = ModNDel;
 | |
| 	else
 | |
| 	  {
 | |
| 	    usage (stderr);
 | |
| 	    return 1;
 | |
| 	  }
 | |
| 	if (! getaclentries (Modify, optarg, acls, &aclidx))
 | |
| 	  {
 | |
| 	    fprintf (stderr, "%s: illegal acl entries\n", prog_name);
 | |
| 	    return 2;
 | |
| 	  }
 | |
| 	break;
 | |
|       case 'n':
 | |
| 	mask_opt = -1;
 | |
| 	break;
 | |
|       case '\n':
 | |
| 	mask_opt = 1;
 | |
| 	break;
 | |
|       case 'r':
 | |
| 	break;
 | |
|       case 's':
 | |
| 	if (action == NoAction)
 | |
| 	  action = Set;
 | |
| 	else
 | |
| 	  {
 | |
| 	    usage (stderr);
 | |
| 	    return 1;
 | |
| 	  }
 | |
| 	if (! getaclentries (Set, optarg, acls, &aclidx))
 | |
| 	  {
 | |
| 	    fprintf (stderr, "%s: illegal acl entries\n", prog_name);
 | |
| 	    return 2;
 | |
| 	  }
 | |
| 	break;
 | |
|       case 'V':
 | |
| 	print_version ();
 | |
| 	return 0;
 | |
|       default:
 | |
| 	fprintf (stderr, "Try `%s --help' for more information.\n", prog_name);
 | |
| 	return 1;
 | |
|       }
 | |
|   if (action == NoAction)
 | |
|     {
 | |
|       usage (stderr);
 | |
|       return 1;
 | |
|     }
 | |
|   if (optind > argc - 1)
 | |
|     {
 | |
|       usage (stderr);
 | |
|       return 1;
 | |
|     }
 | |
|   if (action == Set)
 | |
|     switch (aclcheck (acls, aclidx, NULL))
 | |
|       {
 | |
|       case GRP_ERROR:
 | |
| 	fprintf (stderr, "%s: more than one group entry.\n", prog_name);
 | |
| 	return 2;
 | |
|       case USER_ERROR:
 | |
| 	fprintf (stderr, "%s: more than one user entry.\n", prog_name);
 | |
| 	return 2;
 | |
|       case CLASS_ERROR:
 | |
| 	fprintf (stderr, "%s: more than one mask entry.\n", prog_name);
 | |
| 	return 2;
 | |
|       case OTHER_ERROR:
 | |
| 	fprintf (stderr, "%s: more than one other entry.\n", prog_name);
 | |
| 	return 2;
 | |
|       case DUPLICATE_ERROR:
 | |
| 	fprintf (stderr, "%s: duplicate additional user or group.\n", prog_name);
 | |
| 	return 2;
 | |
|       case ENTRY_ERROR:
 | |
| 	fprintf (stderr, "%s: invalid entry type.\n", prog_name);
 | |
| 	return 2;
 | |
|       case MISS_ERROR:
 | |
| 	fprintf (stderr, "%s: missing entries.\n", prog_name);
 | |
| 	return 2;
 | |
|       case MEM_ERROR:
 | |
| 	fprintf (stderr, "%s: out of memory.\n", prog_name);
 | |
| 	return 2;
 | |
|       default:
 | |
| 	break;
 | |
|       }
 | |
|   for (c = optind; c < argc; ++c)
 | |
|     ret |= setfacl (action, argv[c], acls, aclidx);
 | |
|   return ret;
 | |
| }
 |