📄 acls.c
字号:
/* * Handle passing Access Control Lists between systems. * * Copyright (C) 1996 Andrew Tridgell * Copyright (C) 1996 Paul Mackerras * Copyright (C) 2006-2008 Wayne Davison * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, visit the http://fsf.org website. */#include "rsync.h"#include "lib/sysacls.h"#ifdef SUPPORT_ACLSextern int dry_run;extern int am_root;extern int read_only;extern int list_only;extern int orig_umask;extern int numeric_ids;extern int inc_recurse;/* Flags used to indicate what items are being transmitted for an entry. */#define XMIT_USER_OBJ (1<<0)#define XMIT_GROUP_OBJ (1<<1)#define XMIT_MASK_OBJ (1<<2)#define XMIT_OTHER_OBJ (1<<3)#define XMIT_NAME_LIST (1<<4)#define NO_ENTRY ((uchar)0x80) /* Default value of a NON-name-list entry. */#define NAME_IS_USER (1u<<31) /* Bit used only on a name-list entry. *//* When we send the access bits over the wire, we shift them 2 bits to the * left and use the lower 2 bits as flags (relevant only to a name entry). * This makes the protocol more efficient than sending a value that would * be likely to have its hightest bits set. */#define XFLAG_NAME_FOLLOWS 0x0001u#define XFLAG_NAME_IS_USER 0x0002u/* === ACL structures === */typedef struct { id_t id; uint32 access;} id_access;typedef struct { id_access *idas; int count;} ida_entries;typedef struct { char *name; uchar len;} idname;typedef struct rsync_acl { ida_entries names; /* These will be NO_ENTRY if there's no such entry. */ uchar user_obj; uchar group_obj; uchar mask_obj; uchar other_obj;} rsync_acl;typedef struct { rsync_acl racl; SMB_ACL_T sacl;} acl_duo;static const rsync_acl empty_rsync_acl = { {NULL, 0}, NO_ENTRY, NO_ENTRY, NO_ENTRY, NO_ENTRY};static item_list access_acl_list = EMPTY_ITEM_LIST;static item_list default_acl_list = EMPTY_ITEM_LIST;/* === Calculations on ACL types === */static const char *str_acl_type(SMB_ACL_TYPE_T type){ switch (type) { case SMB_ACL_TYPE_ACCESS:#ifdef HAVE_OSX_ACLS return "ACL_TYPE_EXTENDED";#else return "ACL_TYPE_ACCESS";#endif case SMB_ACL_TYPE_DEFAULT: return "ACL_TYPE_DEFAULT"; default: break; } return "unknown ACL type!";}static int calc_sacl_entries(const rsync_acl *racl){ /* A System ACL always gets user/group/other permission entries. */ return racl->names.count#ifdef ACLS_NEED_MASK + 4;#else + (racl->mask_obj != NO_ENTRY) + 3;#endif}/* Extracts and returns the permission bits from the ACL. This cannot be * called on an rsync_acl that has NO_ENTRY in any spot but the mask. */static int rsync_acl_get_perms(const rsync_acl *racl){ return (racl->user_obj << 6) + ((racl->mask_obj != NO_ENTRY ? racl->mask_obj : racl->group_obj) << 3) + racl->other_obj;}/* Removes the permission-bit entries from the ACL because these * can be reconstructed from the file's mode. */static void rsync_acl_strip_perms(rsync_acl *racl){ racl->user_obj = NO_ENTRY; if (racl->mask_obj == NO_ENTRY) racl->group_obj = NO_ENTRY; else { if (racl->group_obj == racl->mask_obj) racl->group_obj = NO_ENTRY; racl->mask_obj = NO_ENTRY; } racl->other_obj = NO_ENTRY;}/* Given an empty rsync_acl, fake up the permission bits. */static void rsync_acl_fake_perms(rsync_acl *racl, mode_t mode){ racl->user_obj = (mode >> 6) & 7; racl->group_obj = (mode >> 3) & 7; racl->other_obj = mode & 7;}/* === Rsync ACL functions === */static rsync_acl *create_racl(void){ rsync_acl *racl = new(rsync_acl); if (!racl) out_of_memory("create_racl"); *racl = empty_rsync_acl; return racl;}static BOOL ida_entries_equal(const ida_entries *ial1, const ida_entries *ial2){ id_access *ida1, *ida2; int count = ial1->count; if (count != ial2->count) return False; ida1 = ial1->idas; ida2 = ial2->idas; for (; count--; ida1++, ida2++) { if (ida1->access != ida2->access || ida1->id != ida2->id) return False; } return True;}static BOOL rsync_acl_equal(const rsync_acl *racl1, const rsync_acl *racl2){ return racl1->user_obj == racl2->user_obj && racl1->group_obj == racl2->group_obj && racl1->mask_obj == racl2->mask_obj && racl1->other_obj == racl2->other_obj && ida_entries_equal(&racl1->names, &racl2->names);}/* Are the extended (non-permission-bit) entries equal? If so, the rest of * the ACL will be handled by the normal mode-preservation code. This is * only meaningful for access ACLs! Note: the 1st arg is a fully-populated * rsync_acl, but the 2nd parameter can be a condensed rsync_acl, which means * that it might have several of its permission objects set to NO_ENTRY. */static BOOL rsync_acl_equal_enough(const rsync_acl *racl1, const rsync_acl *racl2, mode_t m){ if ((racl1->mask_obj ^ racl2->mask_obj) & NO_ENTRY) return False; /* One has a mask and the other doesn't */ /* When there's a mask, the group_obj becomes an extended entry. */ if (racl1->mask_obj != NO_ENTRY) { /* A condensed rsync_acl with a mask can only have no * group_obj when it was identical to the mask. This * means that it was also identical to the group attrs * from the mode. */ if (racl2->group_obj == NO_ENTRY) { if (racl1->group_obj != ((m >> 3) & 7)) return False; } else if (racl1->group_obj != racl2->group_obj) return False; } return ida_entries_equal(&racl1->names, &racl2->names);}static void rsync_acl_free(rsync_acl *racl){ if (racl->names.idas) free(racl->names.idas); *racl = empty_rsync_acl;}void free_acl(stat_x *sxp){ if (sxp->acc_acl) { rsync_acl_free(sxp->acc_acl); free(sxp->acc_acl); sxp->acc_acl = NULL; } if (sxp->def_acl) { rsync_acl_free(sxp->def_acl); free(sxp->def_acl); sxp->def_acl = NULL; }}#ifdef SMB_ACL_NEED_SORTstatic int id_access_sorter(const void *r1, const void *r2){ id_access *ida1 = (id_access *)r1; id_access *ida2 = (id_access *)r2; id_t rid1 = ida1->id, rid2 = ida2->id; if ((ida1->access ^ ida2->access) & NAME_IS_USER) return ida1->access & NAME_IS_USER ? -1 : 1; return rid1 == rid2 ? 0 : rid1 < rid2 ? -1 : 1;}#endif/* === System ACLs === *//* Unpack system ACL -> rsync ACL verbatim. Return whether we succeeded. */static BOOL unpack_smb_acl(SMB_ACL_T sacl, rsync_acl *racl){ static item_list temp_ida_list = EMPTY_ITEM_LIST; SMB_ACL_ENTRY_T entry; const char *errfun; int rc; errfun = "sys_acl_get_entry"; for (rc = sys_acl_get_entry(sacl, SMB_ACL_FIRST_ENTRY, &entry); rc == 1; rc = sys_acl_get_entry(sacl, SMB_ACL_NEXT_ENTRY, &entry)) { SMB_ACL_TAG_T tag_type; uint32 access; id_t g_u_id; id_access *ida; if ((rc = sys_acl_get_info(entry, &tag_type, &access, &g_u_id)) != 0) { errfun = "sys_acl_get_info"; break; } /* continue == done with entry; break == store in temporary ida list */ switch (tag_type) {#ifndef HAVE_OSX_ACLS case SMB_ACL_USER_OBJ: if (racl->user_obj == NO_ENTRY) racl->user_obj = access; else rprintf(FINFO, "unpack_smb_acl: warning: duplicate USER_OBJ entry ignored\n"); continue; case SMB_ACL_GROUP_OBJ: if (racl->group_obj == NO_ENTRY) racl->group_obj = access; else rprintf(FINFO, "unpack_smb_acl: warning: duplicate GROUP_OBJ entry ignored\n"); continue; case SMB_ACL_MASK: if (racl->mask_obj == NO_ENTRY) racl->mask_obj = access; else rprintf(FINFO, "unpack_smb_acl: warning: duplicate MASK entry ignored\n"); continue; case SMB_ACL_OTHER: if (racl->other_obj == NO_ENTRY) racl->other_obj = access; else rprintf(FINFO, "unpack_smb_acl: warning: duplicate OTHER entry ignored\n"); continue;#endif case SMB_ACL_USER: access |= NAME_IS_USER; break; case SMB_ACL_GROUP: break; default: rprintf(FINFO, "unpack_smb_acl: warning: entry with unrecognized tag type ignored\n"); continue; } ida = EXPAND_ITEM_LIST(&temp_ida_list, id_access, -10); ida->id = g_u_id; ida->access = access; } if (rc) { rsyserr(FERROR_XFER, errno, "unpack_smb_acl: %s()", errfun); rsync_acl_free(racl); return False; } /* Transfer the count id_access items out of the temp_ida_list * into the names ida_entries list in racl. */ if (temp_ida_list.count) {#ifdef SMB_ACL_NEED_SORT if (temp_ida_list.count > 1) { qsort(temp_ida_list.items, temp_ida_list.count, sizeof (id_access), id_access_sorter); }#endif if (!(racl->names.idas = new_array(id_access, temp_ida_list.count))) out_of_memory("unpack_smb_acl"); memcpy(racl->names.idas, temp_ida_list.items, temp_ida_list.count * sizeof (id_access)); } else racl->names.idas = NULL; racl->names.count = temp_ida_list.count; /* Truncate the temporary list now that its idas have been saved. */ temp_ida_list.count = 0;#ifdef ACLS_NEED_MASK if (!racl->names.count && racl->mask_obj != NO_ENTRY) { /* Throw away a superfluous mask, but mask off the * group perms with it first. */ racl->group_obj &= racl->mask_obj; racl->mask_obj = NO_ENTRY; }#endif return True;}/* Synactic sugar for system calls */#define CALL_OR_ERROR(func,args,str) \ do { \ if (func args) { \ errfun = str; \ goto error_exit; \ } \ } while (0)#define COE(func,args) CALL_OR_ERROR(func,args,#func)#define COE2(func,args) CALL_OR_ERROR(func,args,NULL)#ifndef HAVE_OSX_ACLS/* Store the permissions in the system ACL entry. */static int store_access_in_entry(uint32 access, SMB_ACL_ENTRY_T entry){ if (sys_acl_set_access_bits(entry, access)) { rsyserr(FERROR_XFER, errno, "store_access_in_entry sys_acl_set_access_bits()"); return -1; } return 0;}#endif/* Pack rsync ACL -> system ACL verbatim. Return whether we succeeded. */static BOOL pack_smb_acl(SMB_ACL_T *smb_acl, const rsync_acl *racl){#ifdef ACLS_NEED_MASK uchar mask_bits;#endif size_t count; id_access *ida; const char *errfun = NULL; SMB_ACL_ENTRY_T entry; if (!(*smb_acl = sys_acl_init(calc_sacl_entries(racl)))) { rsyserr(FERROR_XFER, errno, "pack_smb_acl: sys_acl_init()"); return False; }#ifndef HAVE_OSX_ACLS COE( sys_acl_create_entry,(smb_acl, &entry) ); COE( sys_acl_set_info,(entry, SMB_ACL_USER_OBJ, racl->user_obj & ~NO_ENTRY, 0) );#endif for (ida = racl->names.idas, count = racl->names.count; count; ida++, count--) {#ifdef SMB_ACL_NEED_SORT if (!(ida->access & NAME_IS_USER)) break;#endif COE( sys_acl_create_entry,(smb_acl, &entry) ); COE( sys_acl_set_info, (entry, ida->access & NAME_IS_USER ? SMB_ACL_USER : SMB_ACL_GROUP, ida->access & ~NAME_IS_USER, ida->id) ); }#ifndef HAVE_OSX_ACLS COE( sys_acl_create_entry,(smb_acl, &entry) ); COE( sys_acl_set_info,(entry, SMB_ACL_GROUP_OBJ, racl->group_obj & ~NO_ENTRY, 0) );#ifdef SMB_ACL_NEED_SORT for ( ; count; ida++, count--) { COE( sys_acl_create_entry,(smb_acl, &entry) ); COE( sys_acl_set_info,(entry, SMB_ACL_GROUP, ida->access, ida->id) ); }#endif#ifdef ACLS_NEED_MASK mask_bits = racl->mask_obj == NO_ENTRY ? racl->group_obj & ~NO_ENTRY : racl->mask_obj; COE( sys_acl_create_entry,(smb_acl, &entry) ); COE( sys_acl_set_info,(entry, SMB_ACL_MASK, mask_bits, NULL) );#else if (racl->mask_obj != NO_ENTRY) { COE( sys_acl_create_entry,(smb_acl, &entry) ); COE( sys_acl_set_info,(entry, SMB_ACL_MASK, racl->mask_obj, 0) ); }#endif COE( sys_acl_create_entry,(smb_acl, &entry) ); COE( sys_acl_set_info,(entry, SMB_ACL_OTHER, racl->other_obj & ~NO_ENTRY, 0) );#endif#ifdef DEBUG if (sys_acl_valid(*smb_acl) < 0) rprintf(FERROR_XFER, "pack_smb_acl: warning: system says the ACL I packed is invalid\n");#endif return True; error_exit: if (errfun) { rsyserr(FERROR_XFER, errno, "pack_smb_acl %s()", errfun); } sys_acl_free_acl(*smb_acl); return False;}static int find_matching_rsync_acl(const rsync_acl *racl, SMB_ACL_TYPE_T type, const item_list *racl_list){ static int access_match = -1, default_match = -1; int *match = type == SMB_ACL_TYPE_ACCESS ? &access_match : &default_match; size_t count = racl_list->count; /* If this is the first time through or we didn't match the last * time, then start at the end of the list, which should be the * best place to start hunting. */ if (*match == -1) *match = racl_list->count - 1; while (count--) { rsync_acl *base = racl_list->items; if (rsync_acl_equal(base + *match, racl)) return *match; if (!(*match)--) *match = racl_list->count - 1; } *match = -1; return *match;}static int get_rsync_acl(const char *fname, rsync_acl *racl, SMB_ACL_TYPE_T type, mode_t mode){ SMB_ACL_T sacl;#ifdef SUPPORT_XATTRS /* --fake-super support: load ACLs from an xattr. */ if (am_root < 0) { char *buf; size_t len; int cnt; if ((buf = get_xattr_acl(fname, type == SMB_ACL_TYPE_ACCESS, &len)) == NULL) return 0; cnt = (len - 4*4) / (4+4); if (len < 4*4 || len != (size_t)cnt*(4+4) + 4*4) { free(buf); return -1; } racl->user_obj = IVAL(buf, 0); racl->group_obj = IVAL(buf, 4); racl->mask_obj = IVAL(buf, 8); racl->other_obj = IVAL(buf, 12); if (cnt) { char *bp = buf + 4*4; id_access *ida; if (!(ida = racl->names.idas = new_array(id_access, cnt))) out_of_memory("get_rsync_acl"); racl->names.count = cnt; for ( ; cnt--; ida++, bp += 4+4) { ida->id = IVAL(bp, 0); ida->access = IVAL(bp, 4); } } free(buf); return 0; }#endif if ((sacl = sys_acl_get_file(fname, type)) != 0) { BOOL ok = unpack_smb_acl(sacl, racl); sys_acl_free_acl(sacl); if (!ok) { return -1; } } else if (no_acl_syscall_error(errno)) { /* ACLs are not supported, so pretend we have a basic ACL. */ if (type == SMB_ACL_TYPE_ACCESS) rsync_acl_fake_perms(racl, mode); } else { rsyserr(FERROR_XFER, errno, "get_acl: sys_acl_get_file(%s, %s)", fname, str_acl_type(type)); return -1; } return 0;}/* Return the Access Control List for the given filename. */int get_acl(const char *fname, stat_x *sxp){ sxp->acc_acl = create_racl(); if (get_rsync_acl(fname, sxp->acc_acl, SMB_ACL_TYPE_ACCESS, sxp->st.st_mode) < 0) { free_acl(sxp); return -1; } if (S_ISDIR(sxp->st.st_mode)) { sxp->def_acl = create_racl(); if (get_rsync_acl(fname, sxp->def_acl, SMB_ACL_TYPE_DEFAULT,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -