--- embedaddon/rsync/acls.c 2012/02/17 15:09:30 1.1.1.1 +++ embedaddon/rsync/acls.c 2016/11/01 09:54:32 1.1.1.3 @@ -3,7 +3,7 @@ * * Copyright (C) 1996 Andrew Tridgell * Copyright (C) 1996 Paul Mackerras - * Copyright (C) 2006-2009 Wayne Davison + * Copyright (C) 2006-2015 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 @@ -78,20 +78,35 @@ typedef struct rsync_acl { uchar other_obj; } rsync_acl; +typedef struct nfs4_acl { + char *nfs4_acl_text; + ssize_t nfs4_acl_len; +} nfs4_acl; + typedef struct { rsync_acl racl; SMB_ACL_T sacl; } acl_duo; +typedef struct { + nfs4_acl nacl; + SMB_ACL_T sacl; +} nfs4_duo; + static const rsync_acl empty_rsync_acl = { {NULL, 0}, NO_ENTRY, NO_ENTRY, NO_ENTRY, NO_ENTRY }; +static const nfs4_acl empty_nfs4_acl = { + NULL, -1 +}; static item_list access_acl_list = EMPTY_ITEM_LIST; static item_list default_acl_list = EMPTY_ITEM_LIST; +static item_list nfs4_acl_list = EMPTY_ITEM_LIST; static size_t prior_access_count = (size_t)-1; static size_t prior_default_count = (size_t)-1; +static size_t prior_nfs4_count = (size_t)-1; /* === Calculations on ACL types === */ @@ -175,6 +190,17 @@ static rsync_acl *create_racl(void) return racl; } +static nfs4_acl *create_nfs4_acl(void) +{ + nfs4_acl *nacl = new(nfs4_acl); + + if (!nacl) + out_of_memory("create_nfs4_acl"); + *nacl = empty_nfs4_acl; + + return nacl; +} + static BOOL ida_entries_equal(const ida_entries *ial1, const ida_entries *ial2) { id_access *ida1, *ida2; @@ -199,6 +225,11 @@ static BOOL rsync_acl_equal(const rsync_acl *racl1, co && ida_entries_equal(&racl1->names, &racl2->names); } +static BOOL nfs4_acl_equal(const nfs4_acl *nacl1, const nfs4_acl *nacl2) +{ + return (strcmp(nacl1->nfs4_acl_text, nacl2->nfs4_acl_text) == 0); +} + /* 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 @@ -232,6 +263,13 @@ static void rsync_acl_free(rsync_acl *racl) *racl = empty_rsync_acl; } +static void nfs4_acl_free(nfs4_acl *nacl) +{ + if (nacl->nfs4_acl_text) + free(nacl->nfs4_acl_text); + *nacl = empty_nfs4_acl; +} + void free_acl(stat_x *sxp) { if (sxp->acc_acl) { @@ -244,6 +282,11 @@ void free_acl(stat_x *sxp) free(sxp->def_acl); sxp->def_acl = NULL; } + if (sxp->nfs4_acl) { + nfs4_acl_free(sxp->nfs4_acl); + free(sxp->nfs4_acl); + sxp->nfs4_acl = NULL; + } } #ifdef SMB_ACL_NEED_SORT @@ -423,7 +466,7 @@ static BOOL pack_smb_acl(SMB_ACL_T *smb_acl, const rsy #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) ); + COE( sys_acl_set_info,(entry, SMB_ACL_MASK, mask_bits, 0) ); #else if (racl->mask_obj != NO_ENTRY) { COE( sys_acl_create_entry,(smb_acl, &entry) ); @@ -474,6 +517,26 @@ static int find_matching_rsync_acl(const rsync_acl *ra return *match; } +static int find_matching_nfs4_acl(const nfs4_acl *nacl, const item_list *nfs4_acl_list) +{ + static int nfs4_match = -1; + int *match = &nfs4_match; + size_t count = nfs4_acl_list->count; + + if (*match == -1) + *match = nfs4_acl_list->count - 1; + while (count--) { + nfs4_acl *base = nfs4_acl_list->items; + if (nfs4_acl_equal(base + *match, nacl)) + return *match; + if (!(*match)--) + *match = nfs4_acl_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) { @@ -544,6 +607,21 @@ static int get_rsync_acl(const char *fname, rsync_acl /* Return the Access Control List for the given filename. */ int get_acl(const char *fname, stat_x *sxp) { + if (sys_acl_get_brand_file(fname, &sxp->brand) < 0) + return -1; + + if (sxp->brand == SMB_ACL_BRAND_NFS4) { + SMB_ACL_T sacl; + if ((sacl = sys_acl_get_file(fname, SMB_ACL_TYPE_NFS4)) == NULL) + return -1; + + sxp->nfs4_acl = create_nfs4_acl(); + sxp->nfs4_acl->nfs4_acl_text = acl_to_text(sacl, &sxp->nfs4_acl->nfs4_acl_len); + + sys_acl_free_acl(sacl); + return 0; + } + sxp->acc_acl = create_racl(); if (S_ISREG(sxp->st.st_mode) || S_ISDIR(sxp->st.st_mode)) { @@ -560,7 +638,8 @@ int get_acl(const char *fname, stat_x *sxp) if (!preserve_devices) #endif return 0; - } + } else if (IS_MISSING_FILE(sxp->st)) + return 0; if (get_rsync_acl(fname, sxp->acc_acl, SMB_ACL_TYPE_ACCESS, sxp->st.st_mode) < 0) { @@ -651,6 +730,25 @@ static void send_rsync_acl(int f, rsync_acl *racl, SMB } } +static void send_nfs4_acl(int f, nfs4_acl *nacl, item_list *nfs4_list) +{ + int ndx = find_matching_nfs4_acl(nacl, nfs4_list); + + /* Send 0 (-1 + 1) to indicate that literal ACL data follows. */ + write_varint(f, ndx + 1); + + if (ndx < 0) { + nfs4_acl *new_nacl = EXPAND_ITEM_LIST(&nfs4_acl_list, nfs4_acl, 1000); + + write_varint(f, nacl->nfs4_acl_len); + write_buf(f, nacl->nfs4_acl_text, nacl->nfs4_acl_len); + + *new_nacl = *nacl; + *nacl = empty_nfs4_acl; + } +} + + /* Send the ACL from the stat_x structure down the indicated file descriptor. * This also frees the ACL data. */ void send_acl(int f, stat_x *sxp) @@ -662,12 +760,14 @@ void send_acl(int f, stat_x *sxp) /* Avoid sending values that can be inferred from other data. */ rsync_acl_strip_perms(sxp); + write_varint(f, SMB_ACL_TYPE_ACCESS); send_rsync_acl(f, sxp->acc_acl, SMB_ACL_TYPE_ACCESS, &access_acl_list); if (S_ISDIR(sxp->st.st_mode)) { if (!sxp->def_acl) sxp->def_acl = create_racl(); + write_varint(f, SMB_ACL_TYPE_DEFAULT); send_rsync_acl(f, sxp->def_acl, SMB_ACL_TYPE_DEFAULT, &default_acl_list); } } @@ -817,10 +917,37 @@ static int cache_rsync_acl(rsync_acl *racl, SMB_ACL_TY return ndx; } +static int cache_nfs4_acl(nfs4_acl *nacl, item_list *nfs4_list) +{ + int ndx; + + if (!nacl) + ndx = -1; + else if ((ndx = find_matching_nfs4_acl(nacl, nfs4_list)) == -1) { + nfs4_duo *new_duo; + ndx = nfs4_list->count; + new_duo = EXPAND_ITEM_LIST(nfs4_list, nfs4_duo, 1000); + new_duo->nacl = *nacl; + new_duo->sacl = NULL; + *nacl = empty_nfs4_acl; + } + + return ndx; +} + + /* Turn the ACL data in stat_x into cached ACL data, setting the index * values in the file struct. */ void cache_tmp_acl(struct file_struct *file, stat_x *sxp) { + if (sxp->brand == SMB_ACL_BRAND_NFS4) { + if (prior_nfs4_count == (size_t)-1) + prior_nfs4_count = nfs4_acl_list.count; + + F_ACL(file) = cache_nfs4_acl(sxp->nfs4_acl, &nfs4_acl_list); + return; + } + if (prior_access_count == (size_t)-1) prior_access_count = access_acl_list.count; @@ -850,6 +977,21 @@ static void uncache_duo_acls(item_list *duo_list, size } } +static void uncache_nfs4_acls(item_list *nfs4_list, size_t start) +{ + nfs4_duo *nfs4_item = nfs4_list->items; + nfs4_duo *nfs4_start = nfs4_item + start; + + nfs4_item += nfs4_list->count; + nfs4_list->count = start; + + while (nfs4_item-- > nfs4_start) { + nfs4_acl_free(&nfs4_item->nacl); + if (nfs4_item->sacl) + sys_acl_free_acl(nfs4_item->sacl); + } +} + void uncache_tmp_acls(void) { if (prior_access_count != (size_t)-1) { @@ -861,6 +1003,10 @@ void uncache_tmp_acls(void) uncache_duo_acls(&default_acl_list, prior_default_count); prior_default_count = (size_t)-1; } + if (prior_nfs4_count != (size_t)-1) { + uncache_nfs4_acls(&nfs4_acl_list, prior_nfs4_count); + prior_nfs4_count = (size_t)-1; + } } #ifndef HAVE_OSX_ACLS @@ -1013,6 +1159,7 @@ static int set_rsync_acl(const char *fname, acl_duo *d return 0; } + /* Given a fname, this sets extended access ACL entries, the default ACL (for a * dir), and the regular mode bits on the file. Call this with fname set to * NULL to just check if the ACL is different. @@ -1032,6 +1179,32 @@ int set_acl(const char *fname, const struct file_struc return -1; } + if (sxp->brand == SMB_ACL_BRAND_NFS4) { + ndx = F_ACL(file); + if (ndx >= 0 && (size_t)ndx < nfs4_acl_list.count) { + nfs4_duo *duo_item = nfs4_acl_list.items; + duo_item += ndx; + changed = 1; + + if (!duo_item->sacl) { + duo_item->sacl = acl_from_text(duo_item->nacl.nfs4_acl_text); + if (!duo_item->sacl) + return -1; + } + + if (!dry_run && fname) { + if (sys_acl_set_file(fname, SMB_ACL_TYPE_NFS4, duo_item->sacl) < 0) { + rsyserr(FERROR_XFER, errno, "set_acl: sys_acl_set_file(%s, %s)", + fname, str_acl_type(SMB_ACL_TYPE_NFS4)); + return -1; + } + + return changed; + } + } + } + + ndx = F_ACL(file); if (ndx >= 0 && (size_t)ndx < access_acl_list.count) { acl_duo *duo_item = access_acl_list.items; @@ -1143,7 +1316,7 @@ int default_perms_for_dir(const char *dir) /* Apply the permission-bit entries of the default ACL, if any. */ if (racl.user_obj != NO_ENTRY) { perms = rsync_acl_get_perms(&racl); - if (verbose > 2) + if (DEBUG_GTE(ACL, 1)) rprintf(FINFO, "got ACL-based default perms %o for directory %s\n", perms, dir); }