Example #1
0
def get_acl_lists_from_rp(rp):
    """Returns (acl_list, def_acl_list) from an rpath.  Call locally"""
    assert rp.conn is Globals.local_connection
    try:
        acl = posix1e.ACL(file=rp.path)
    except (FileNotFoundError, UnicodeEncodeError) as exc:
        log.Log(
            "Warning: unable to read ACL from %s: %s" %
            (rp.get_safepath(), exc), 3)
        acl = None
    except IOError as exc:
        if exc.errno == errno.EOPNOTSUPP:
            acl = None
        else:
            raise
    if rp.isdir():
        try:
            def_acl = posix1e.ACL(filedef=os.fsdecode(rp.path))
        except (FileNotFoundError, UnicodeEncodeError) as exc:
            log.Log(
                "Warning: unable to read default ACL from %s: %s" %
                (rp.get_safepath(), exc), 3)
            def_acl = None
        except IOError as exc:
            if exc.errno == errno.EOPNOTSUPP:
                def_acl = None
            else:
                raise
    else:
        def_acl = None
    return (acl and acl_to_list(acl), def_acl and acl_to_list(def_acl))
Example #2
0
 def testFromDir(self):
     """Test loading ACLs from a directory"""
     dname = self._getdir()
     acl1 = posix1e.ACL(file=dname)
     acl2 = posix1e.ACL(filedef=dname)
     self.assertTrue(acl1.valid(),
                     "ACL read from directory should be valid")
Example #3
0
def get_acl_lists_from_rp(rp):
    """Returns (acl_list, def_acl_list) from an rpath.  Call locally"""
    assert rp.conn is Globals.local_connection, (
        "Get ACLs of path should only be done locally not over {conn}.".format(
            conn=rp.conn))
    try:
        acl = posix1e.ACL(file=rp.path)
    except (FileNotFoundError, UnicodeEncodeError) as exc:
        log.Log(
            "Unable to read ACL from path {pa} due to exception '{ex}'".format(
                pa=rp, ex=exc), log.NOTE)
        acl = None
    except OSError as exc:
        if exc.errno == errno.EOPNOTSUPP:
            acl = None
        else:
            raise
    if rp.isdir():
        try:
            def_acl = posix1e.ACL(filedef=os.fsdecode(rp.path))
        except (FileNotFoundError, UnicodeEncodeError) as exc:
            log.Log(
                "Unable to read default ACL from path {pa} due to "
                "exception '{ex}'".format(pa=rp, ex=exc), log.NOTE)
            def_acl = None
        except OSError as exc:
            if exc.errno == errno.EOPNOTSUPP:
                def_acl = None
            else:
                raise
    else:
        def_acl = None
    return (acl and _acl_to_list(acl), def_acl and _acl_to_list(def_acl))
Example #4
0
def set_rp_acl(rp, entry_list=None, default_entry_list=None, map_names=1):
    """Set given rp with ACL that acl_text defines.  rp should be local"""
    assert rp.conn is Globals.local_connection
    if entry_list:
        acl = list_to_acl(entry_list, map_names)
    else:
        acl = posix1e.ACL()

    try:
        acl.applyto(rp.path)
    except IOError as exc:
        if exc.errno == errno.EOPNOTSUPP:
            log.Log(
                "Warning: unable to set ACL on %s: %s" %
                (rp.get_safepath(), exc), 4)
            return
        else:
            raise

    if rp.isdir():
        if default_entry_list:
            def_acl = list_to_acl(default_entry_list, map_names)
        else:
            def_acl = posix1e.ACL()
        def_acl.applyto(rp.path, posix1e.ACL_TYPE_DEFAULT)
Example #5
0
 def _add_posix1e_acl(self, path, st):
     if not posix1e or not posix1e.HAS_EXTENDED_CHECK:
         return
     if not stat.S_ISLNK(st.st_mode):
         acls = None
         def_acls = None
         try:
             if posix1e.has_extended(path):
                 acl = posix1e.ACL(file=path)
                 acls = [acl, acl]  # txt and num are the same
                 if stat.S_ISDIR(st.st_mode):
                     def_acl = posix1e.ACL(filedef=path)
                     def_acls = [def_acl, def_acl]
         except EnvironmentError as e:
             if e.errno not in (errno.EOPNOTSUPP, errno.ENOSYS):
                 raise
         if acls:
             txt_flags = posix1e.TEXT_ABBREVIATE
             num_flags = posix1e.TEXT_ABBREVIATE | posix1e.TEXT_NUMERIC_IDS
             acl_rep = [
                 acls[0].to_any_text('', '\n', txt_flags),
                 acls[1].to_any_text('', '\n', num_flags)
             ]
             if def_acls:
                 acl_rep.append(def_acls[0].to_any_text(
                     '', '\n', txt_flags))
                 acl_rep.append(def_acls[1].to_any_text(
                     '', '\n', num_flags))
             self.posix1e_acl = acl_rep
Example #6
0
def set_rp_acl(rp, entry_list=None, default_entry_list=None, map_names=1):
    """Set given rp with ACL that acl_text defines.  rp should be local"""
    assert rp.conn is Globals.local_connection, (
        "Set ACLs of path should only be done locally not over {conn}.".format(
            conn=rp.conn))
    if entry_list:
        acl = _list_to_acl(entry_list, map_names)
    else:
        acl = posix1e.ACL()

    try:
        acl.applyto(rp.path)
    except OSError as exc:
        if exc.errno == errno.EOPNOTSUPP:
            log.Log(
                "Unable to set ACL on path {pa} due to exception '{ex}'".
                format(pa=rp, ex=exc), log.INFO)
            return
        else:
            raise

    if rp.isdir():
        if default_entry_list:
            def_acl = _list_to_acl(default_entry_list, map_names)
        else:
            def_acl = posix1e.ACL()
        def_acl.applyto(rp.path, posix1e.ACL_TYPE_DEFAULT)
Example #7
0
        def test_dir_posix_acl(self):

        	''' set POSIX acl, get remove ACL on a dir '''

                os.mkdir(TESTDIR, 0755)
                dacl = posix1e.ACL(text=DACL_TO_SET)
                dacl.applyto(TESTDIR)
		got_acl = posix1e.ACL(file=TESTDIR)
                os.rmdir(TESTDIR)
Example #8
0
 def testReapply(self):
     """Test re-applying an ACL"""
     fd, fname = self._getfile()
     acl1 = posix1e.ACL(fd=fd)
     acl1.applyto(fd)
     acl1.applyto(fname)
     dname = self._getdir()
     acl2 = posix1e.ACL(file=fname)
     acl2.applyto(dname)
Example #9
0
 def testEquivMode(self):
     """Test the equiv_mode function"""
     if HAS_ACL_FROM_MODE:
         for mode in M0644, M0755:
             acl = posix1e.ACL(mode=mode)
             self.assertEqual(acl.equiv_mode(), mode)
     acl = posix1e.ACL(text="u::rw,g::r,o::r")
     self.assertEqual(acl.equiv_mode(), M0644)
     acl = posix1e.ACL(text="u::rx,g::-,o::-")
     self.assertEqual(acl.equiv_mode(), M0500)
Example #10
0
        def test_file_posix_acl(self):

        	''' set POSIX acl, get remove ACL on a file '''

                f = open(TESTFILE, 'w')
                facl = posix1e.ACL(text=FACL_TO_SET)
                facl.applyto(TESTFILE)
		got_acl = posix1e.ACL(file=TESTFILE)
                f.close()
                safe_rm(TESTFILE)
Example #11
0
 def _add_posix1e_acl(self, path, st):
     if not posix1e: return
     if not stat.S_ISLNK(st.st_mode):
         try:
             if posix1e.has_extended(path):
                 acl = posix1e.ACL(file=path)
                 self.posix1e_acl = [acl, acl] # txt and num are the same
                 if stat.S_ISDIR(st.st_mode):
                     acl = posix1e.ACL(filedef=path)
                     self.posix1e_acl.extend([acl, acl])
         except EnvironmentError, e:
             if e.errno != errno.EOPNOTSUPP:
                 raise
Example #12
0
def acls_from_file(filename, include_standard=False):
    """Returns the extended ACL entries from the given
       file as list of the text representation.

       Arguments:
       filename -- the file name to get the ACLs from
       include_standard -- if True, ACL entries representing 
                           standard Linux permissions will be
                           included"""
    result = []
    try:
        acl = posix1e.ACL(file=filename)
    except:
        print 'Error getting ACLs from %s' % filename
        return []

    text = acl.to_any_text(options=posix1e.TEXT_ABBREVIATE
                           | posix1e.TEXT_NUMERIC_IDS)

    for entry in text.split("\n"):
        if not include_standard and \
           re.search(r'^[ugo]::', entry) != None:
            continue
        result.append(entry)

    return result
Example #13
0
    def getACLOnShare(self, name):
        """
        Return a list with all the groups that have rwx access to the share.

        @param name: name of the share (last component of the path)
        @type name: str

        @rtype: tuple
        @return: tuple of groups, users that have rwx access to the share.
        """
        path = self.getContent(name, "path")
        ret = ([], [])
        ldapobj = ldapUserGroupControl()
        acl1 = posix1e.ACL(file=path)
        for e in acl1:
            if e.permset.write:
                if e.tag_type == posix1e.ACL_GROUP:
                    res = ldapobj.getDetailedGroupById(str(e.qualifier))
                    if res:
                        ret[0].append(res['cn'][0])
                    else:
                        ret[0].append(grp.getgrgid(e.qualifier).gr_name)
                if e.tag_type == posix1e.ACL_USER:
                    res = ldapobj.getDetailedUserById(str(e.qualifier))
                    if res:
                        ret[1].append(res['uid'][0])
                    else:
                        ret[1].append(pwd.getpwuid(e.qualifier).pw_name)

        return ret
Example #14
0
 def testExtended(self):
     """Test the acl_extended function"""
     fd, fname = self._getfile()
     basic_acl = posix1e.ACL(text=BASIC_ACL_TEXT)
     basic_acl.applyto(fd)
     for item in fd, fname:
         self.assertFalse(
             has_extended(item),
             "A simple ACL should not be reported as extended")
     enhanced_acl = posix1e.ACL(text="u::rw,g::-,o::-,u:root:rw,mask::r")
     self.assertTrue(enhanced_acl.valid(),
                     "Failure to build an extended ACL")
     enhanced_acl.applyto(fd)
     for item in fd, fname:
         self.assertTrue(has_extended(item),
                         "An extended ACL should be reported as such")
Example #15
0
    def _detect_acls(self, rp):
        """
        Set self.acls based on rp.

        Does not write. Needs to be local
        """
        if not Globals.acls_active:
            log.Log(
                "POSIX ACLs test skipped as rdiff-backup was started "
                "with --no-acls option", log.INFO)
            self.acls = None
            return

        try:
            import posix1e
        except ImportError:
            log.Log(
                "Unable to import module posix1e from pylibacl package. "
                "POSIX ACLs not supported on filesystem at "
                "path {pa}".format(pa=rp), log.INFO)
            self.acls = False
            return

        try:
            posix1e.ACL(file=rp.path)
        except OSError as exc:
            log.Log(
                "POSIX ACLs not supported by filesystem at path {pa} "
                "due to exception '{ex}'".format(pa=rp, ex=exc), log.INFO)
            self.acls = False
        else:
            self.acls = True
    def _detect_acls(self, rp):
        """Set self.acls based on rp.  Does not write.  Needs to be local"""
        if Globals.acls_active == 0:
            log.Log(
                "POSIX ACLs test skipped. rdiff-backup run "
                "with --no-acls option.", 4)
            self.acls = 0
            return

        try:
            import posix1e
        except ImportError:
            log.Log(
                "Unable to import module posix1e from pylibacl "
                "package.\nPOSIX ACLs not supported on filesystem at %s" %
                rp.get_safepath(), 4)
            self.acls = 0
            return

        try:
            posix1e.ACL(file=rp.path)
        except IOError:
            log.Log(
                "POSIX ACLs not supported by filesystem at %s" %
                rp.get_safepath(), 4)
            self.acls = 0
        else:
            self.acls = 1
Example #17
0
 def testMultipleBadEntries(self):
     """Test multiple invalid entries"""
     for tag_type in (posix1e.ACL_USER, posix1e.ACL_GROUP):
         acl = posix1e.ACL(text=BASIC_ACL_TEXT)
         self.assertTrue(
             acl.valid(), "ACL built from standard description"
             " should be valid")
         e1 = acl.append()
         e1.tag_type = tag_type
         e1.qualifier = 0
         e1.permset.clear()
         acl.calc_mask()
         self.assertTrue(acl.valid(), "ACL should be able to add a"
                         " user/group entry")
         e2 = acl.append()
         e2.tag_type = tag_type
         e2.qualifier = 0
         e2.permset.clear()
         ignore_ioerror(errno.EINVAL, acl.calc_mask)
         self.assertFalse(
             acl.valid(), "ACL should not validate when"
             " containing two duplicate entries")
         acl.delete_entry(e1)
         # FreeBSD trips over itself here and can't delete the
         # entry, even though it still exists.
         ignore_ioerror(errno.EINVAL, acl.delete_entry, e2)
Example #18
0
 def testQualifierValues(self):
     """Tests qualifier correct store/retrieval"""
     acl = posix1e.ACL()
     e = acl.append()
     # work around deprecation warnings
     if hasattr(self, 'assertRegex'):
         fn = self.assertRegex
     else:
         fn = self.assertRegexpMatches
     for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
         qualifier = 1
         e.tag_type = tag
         while True:
             if tag == posix1e.ACL_USER:
                 regex = re.compile("user with uid %d" % qualifier)
             else:
                 regex = re.compile("group with gid %d" % qualifier)
             try:
                 e.qualifier = qualifier
             except OverflowError:
                 # reached overflow condition, break
                 break
             self.assertEqual(e.qualifier, qualifier)
             fn(str(e), regex)
             qualifier *= 2
Example #19
0
 def testPermset(self):
     """Test permissions"""
     acl = posix1e.ACL()
     e = acl.append()
     ps = e.permset
     ps.clear()
     str_ps = str(ps)
     self.checkRef(str_ps)
     pmap = {
         posix1e.ACL_READ: "read",
         posix1e.ACL_WRITE: "write",
         posix1e.ACL_EXECUTE: "execute",
     }
     for perm in pmap:
         str_ps = str(ps)
         self.checkRef(str_ps)
         self.assertFalse(
             ps.test(perm), "Empty permission set should not"
             " have permission '%s'" % pmap[perm])
         ps.add(perm)
         self.assertTrue(
             ps.test(perm), "Permission '%s' should exist"
             " after addition" % pmap[perm])
         str_ps = str(ps)
         self.checkRef(str_ps)
         ps.delete(perm)
         self.assertFalse(
             ps.test(perm), "Permission '%s' should not exist"
             " after deletion" % pmap[perm])
Example #20
0
def set_access(root, owner, read_write, read_only, public):
    """
    Recursively changes the owner of all files to the current user, since
    only a file's owner can set its ACL.
    Clears and resets the ACL for each file/directory in the project.
    Recursively changes the owner of all files to the project's owner.
    """
    # Don't allow top-level files/dirs to be symbolic links
    if os.path.islink(root):
        fail("%s is a symbolic link. Cannot update ACL")

    texts = []
    for x in ('-', 'x'):
        for w in ('-', 'w'):
            pieces = ['u::r%s%s,g::r%s%s' % (w, x, w, x)]
            if public:
                pieces.append(',o::r-%s' % x)
            else:
                pieces.append(',o::---')

            # Owners and Members have read/write
            writers = read_write + [owner]
            for user in writers:
                pieces.append(',u:%s:r%s%s' % (user, w, x))
            # Collaborators have read-only
            for user in read_only:
                pieces.append(',u:%s:r-%s' % (user, x))

            texts.append(''.join(pieces))

    gen = []
    for text in texts:
        try:
            acl = posix1e.ACL(text=text)
        except:
            fail(
                "Failed to create ACL. Check project config file for non-existent users"
            )
        acl.calc_mask()

        if not acl.valid():
            logger.debug("Bad ACL: %s" % text)
            fail("Error generating ACL. Please notify system administrator.")
        gen.append(acl)

    ro, rw, rx, rwx = gen[0], gen[1], gen[2], gen[3]

    apply_acl(root, ro, rw, rx, rwx)

    for top, dirs, files in os.walk(root):
        for name in dirs + files:
            path = os.path.join(top, name)
            realpath = os.path.realpath(os.path.expanduser(path))
            if is_subdir(PROJECT_ROOT, realpath):
                apply_acl(path, ro, rw, rx, rwx)
            else:
                logger.warning(
                    "%s is actually %s, which is not in $PROJECT_ROOT" %
                    (path, realpath))
Example #21
0
 def testAppend(self):
     """Test append a new Entry to the ACL"""
     acl = posix1e.ACL()
     e = acl.append()
     e.tag_type = posix1e.ACL_OTHER
     ignore_ioerror(errno.EINVAL, acl.calc_mask)
     str_format = str(e)
     self.checkRef(str_format)
Example #22
0
 def testDelete(self):
     """Test delete Entry from the ACL"""
     acl = posix1e.ACL()
     e = acl.append()
     e.tag_type = posix1e.ACL_OTHER
     ignore_ioerror(errno.EINVAL, acl.calc_mask)
     acl.delete_entry(e)
     ignore_ioerror(errno.EINVAL, acl.calc_mask)
Example #23
0
def list_to_acl(entry_list, map_names=1):
    """Return posix1e.ACL object from list representation

	If map_names is true, use user_group to update the names for the
	current system, and drop if not available.  Otherwise just use the
	same id.

	See the acl_to_list function for the format of an acllist.

	"""
    def char_to_acltag(typechar):
        """Given typechar, query posix1e module for appropriate constant"""
        if typechar == "U": return posix1e.ACL_USER_OBJ
        elif typechar == "u": return posix1e.ACL_USER
        elif typechar == "G": return posix1e.ACL_GROUP_OBJ
        elif typechar == "g": return posix1e.ACL_GROUP
        elif typechar == "M": return posix1e.ACL_MASK
        else:
            assert typechar == "O", typechar
            return posix1e.ACL_OTHER

    def warn_drop(name):
        """Warn about acl with name getting dropped"""
        global dropped_acl_names
        if Globals.never_drop_acls:
            log.Log.FatalError(
                "--never-drop-acls specified but cannot map name\n"
                "%s occurring inside an ACL." % (name, ))
        if dropped_acl_names.has_key(name): return
        log.Log(
            "Warning: name %s not found on system, dropping ACL entry.\n"
            "Further ACL entries dropped with this name will not "
            "trigger further warnings" % (name, ), 2)
        dropped_acl_names[name] = name

    acl = posix1e.ACL()
    for typechar, owner_pair, perms in entry_list:
        id = None
        if owner_pair:
            if map_names:
                if typechar == "u": id = user_group.acl_user_map(*owner_pair)
                else:
                    assert typechar == "g", (typechar, owner_pair, perms)
                    id = user_group.acl_group_map(*owner_pair)
                if id is None:
                    warn_drop(owner_pair[1])
                    continue
            else:
                assert owner_pair[0] is not None, (typechar, owner_pair, perms)
                id = owner_pair[0]

        entry = posix1e.Entry(acl)
        entry.tag_type = char_to_acltag(typechar)
        if id is not None: entry.qualifier = id
        entry.permset.read = perms >> 2
        entry.permset.write = perms >> 1 & 1
        entry.permset.execute = perms & 1
    return acl
Example #24
0
    def _list_file_acls(self, path):
        """ Given a path, get a dict of existing POSIX ACLs on that
        path.  The dict keys are a tuple of (<acl type (access or
        default)>, <acl scope (user or group)>, <acl qualifer (the
        user or group it applies to)>.  values are the permissions of
        the described ACL. """
        def _process_acl(acl, atype):
            """ Given an ACL object, process it appropriately and add
            it to the return value """
            try:
                qual = ''
                if acl.tag_type == posix1e.ACL_USER:
                    qual = pwd.getpwuid(acl.qualifier)[0]
                elif acl.tag_type == posix1e.ACL_GROUP:
                    qual = grp.getgrgid(acl.qualifier)[0]
                elif atype == "access" or acl.tag_type == posix1e.ACL_MASK:
                    return
            except (OSError, KeyError):
                err = sys.exc_info()[1]
                self.logger.error("POSIX: Lookup of %s %s failed: %s" %
                                  (atype, acl.qualifier, err))
                qual = acl.qualifier
            existing[(atype, acl.tag_type, qual)] = \
                self._norm_acl_perms(acl.permset)

        existing = dict()
        try:
            for acl in posix1e.ACL(file=path):
                _process_acl(acl, "access")
        except IOError:
            err = sys.exc_info()[1]
            if err.errno == 95:
                # fs is mounted noacl
                self.logger.debug("POSIX: Filesystem mounted without ACL "
                                  "support: %s" % path)
            else:
                self.logger.error(
                    "POSIX: Error getting current ACLS on %s: %s" %
                    (path, err))
            return existing

        if os.path.isdir(path):
            for acl in posix1e.ACL(filedef=path):
                _process_acl(acl, "default")
        return existing
Example #25
0
 def testQualifierOverflow(self):
     """Tests qualifier overflow handling"""
     acl = posix1e.ACL()
     e = acl.append()
     qualifier = sys.maxsize * 2
     for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
         e.tag_type = tag
         with self.assertRaises(OverflowError):
             e.qualifier = qualifier
def _reset_acl(fn, s, append):
    file_acl = posix1e.ACL(file=fn)
    acl = posix1e.ACL(text=s)

    for entry in acl:
        _remove_dup(file_acl, entry)
        if append:
            file_acl.append(entry)

    file_acl.calc_mask()  # Przelicz maskÄ™.

    if not file_acl.valid():
        print("invalid ACL provided: %s" % acl)
        return False

    # Zaaplikuj przeliczone ACL.
    file_acl.applyto(fn)
    return True
Example #27
0
def get_permissions(path, user, check_prefixes=False):
    user = pwd.getpwnam(user)
    parts = path.split(os.path.sep)
    prefixes = [
        os.path.sep.join(parts[:i + 1]) or '/' for i in xrange(len(parts) - 1)
    ]

    if check_prefixes:
        for prefix in prefixes:
            acl = posix1e.ACL(file=prefix)
            st = os.stat(prefix)
            if not _check_permission(acl, user, st, (posix1e.ACL_EXECUTE, )):
                raise IOError(errno.EACCES, None, prefix)

    acl = posix1e.ACL(file=path.encode('utf-8'))
    st = os.stat(path)

    return set(p for p in (posix1e.ACL_READ, posix1e.ACL_WRITE, posix1e.ACL_EXECUTE) if \
               _check_permission(acl, user, st, (p,)))
Example #28
0
 def testNegativeQualifier(self):
     """Tests negative qualifier handling"""
     # Note: this presumes that uid_t/gid_t in C are unsigned...
     acl = posix1e.ACL()
     e = acl.append()
     for tag in [posix1e.ACL_USER, posix1e.ACL_GROUP]:
         e.tag_type = tag
         for qualifier in [-10, -5, -1]:
             with self.assertRaises(OverflowError):
                 e.qualifier = qualifier
def check_acl(filename, username, log):
    '''
  Check which ACLs given user has for given file.

  @param filename		Absolute path to file to read ACL from
  @param username		Username to check ACL against
  @param log		req.log_error
  '''

    # Get ACL and traditional group lists for the file
    file_acl = posix1e.ACL(file=filename)
    stat_info = os.stat(filename)
    unix_group = grp.getgrgid(stat_info.st_gid).gr_name

    # Sanitize username before invoking shell
    valid_chars = "-_.() %s%s" % (string.ascii_letters, string.digits)
    username = ''.join(c for c in username if c in valid_chars)

    # List user's groups with 'id' command
    p = Popen(["id", "-n", "-G", "-z", username],
              stdin=PIPE,
              stdout=PIPE,
              stderr=PIPE)
    output, err = p.communicate(b'')
    user_groups = str(output).split("\0")

    # Walk through file's ACLs and compare to user's
    can_read = False
    can_write = False

    for l in [s.strip() for s in str(file_acl).splitlines()]:
        group = None
        perm = ''

        # Handle other ("everyone")
        if l[0:7] == 'other::':
            parts = l.split(':')
            perm = parts[2]

        # Handle actual group memberships
        elif l[0:6] == 'group:':
            parts = l.split(':')
            group = parts[1]

            # Check default unix group in addition to ACLs
            if group == '':
                group = unix_group

            if group in user_groups:
                perm = parts[2]

        can_write = can_write or ('w' in perm)
        can_read = can_read or ('r' in perm)

    return {'r': can_read, 'w': can_write}
Example #30
0
 def apply_acl(acl_rep, kind):
     try:
         acl = posix1e.ACL(text=acl_rep)
     except IOError, e:
         if e.errno == 0:
             # pylibacl appears to return an IOError with errno
             # set to 0 if a group referred to by the ACL rep
             # doesn't exist on the current system.
             raise ApplyError("POSIX1e ACL: can't create %r for %r" %
                              (acl_rep, path))
         else:
             raise