/* * Handle passing Access Control Lists between systems. * * Copyright (C) 1996 Andrew Tridgell * Copyright (C) 1996 Paul Mackerras * Copyright (C) 2006-2022 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"#include "lib/acl.h"#ifdef HAVE_LIBACL_AT#include <fcntl.h> /* AT_EMPTY_PATH / AT_SYMLINK_NOFOLLOW */#endif #ifdef SUPPORT_ACLS extern int dry_run;extern int am_root;extern int read_only;extern int list_only;extern mode_t orig_umask;extern int numeric_ids;extern int inc_recurse;extern int preserve_devices;extern int preserve_specials; /* 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 highest 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; static size_t prior_access_count = (size_t)-1;static size_t prior_default_count = (size_t)-1; /* === 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 + 1#else + (racl->mask_obj != NO_ENTRY)#endif + 3;} /* 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(stat_x *sxp){ rsync_acl *racl = sxp->acc_acl; racl->user_obj = NO_ENTRY; if (racl->mask_obj == NO_ENTRY) racl->group_obj = NO_ENTRY; else { int group_perms = (sxp->st.st_mode >> 3) & 7; if (racl->group_obj == group_perms) racl->group_obj = NO_ENTRY;#ifndef HAVE_SOLARIS_ACLS if (racl->names.count != 0 && racl->mask_obj == group_perms) racl->mask_obj = NO_ENTRY;#endif } 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); *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 racl->names.idas = new_array(id_access, temp_ida_list.count); 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; 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, 0) );#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;} /* These two bridge lib/acl.c's neutral (tag,perm,id) entry array; with * HAVE_LIBACL_AT the libacl *_at path uses unpack_smb_acl/pack_smb_acl directly, * so they are unused there. */#if defined(SUPPORT_ACL_FD) && !defined(HAVE_LIBACL_AT)/* Convert a packed system ACL into the neutral (tag,perm,id) entry array that * lib/acl.c serializes. Reuses pack_smb_acl()+change_sacl_perms() output so * the bytes we write match exactly what acl_set_file() would have written. * Returns the entry count and a malloc'd array in *ents_p, or -1 on error. */static int sacl_to_entries(SMB_ACL_T sacl, rsync_acl_ent **ents_p){ static item_list ent_list = EMPTY_ITEM_LIST; SMB_ACL_ENTRY_T entry; rsync_acl_ent *out; int rc; ent_list.count = 0; 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; rsync_acl_ent *e; uint16_t tag; if ((rc = sys_acl_get_info(entry, &tag_type, &access, &g_u_id)) != 0)Lines 1–500 of 1510. Load more
/* * Handle passing Access Control Lists between systems. * * Copyright (C) 1996 Andrew Tridgell * Copyright (C) 1996 Paul Mackerras * Copyright (C) 2006-2022 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" #include "lib/acl.h" #ifdef HAVE_LIBACL_AT #include <fcntl.h> /* AT_EMPTY_PATH / AT_SYMLINK_NOFOLLOW */ #endif #ifdef SUPPORT_ACLS extern int dry_run; extern int am_root; extern int read_only; extern int list_only; extern mode_t orig_umask; extern int numeric_ids; extern int inc_recurse; extern int preserve_devices; extern int preserve_specials; /* 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 highest 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; static size_t prior_access_count = (size_t)-1; static size_t prior_default_count = (size_t)-1; /* === 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 + 1 #else + (racl->mask_obj != NO_ENTRY) #endif + 3; } /* 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(stat_x *sxp) { rsync_acl *racl = sxp->acc_acl; racl->user_obj = NO_ENTRY; if (racl->mask_obj == NO_ENTRY) racl->group_obj = NO_ENTRY; else { int group_perms = (sxp->st.st_mode >> 3) & 7; if (racl->group_obj == group_perms) racl->group_obj = NO_ENTRY; #ifndef HAVE_SOLARIS_ACLS if (racl->names.count != 0 && racl->mask_obj == group_perms) racl->mask_obj = NO_ENTRY; #endif } 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); *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_SORT static 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 racl->names.idas = new_array(id_access, temp_ida_list.count); 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; 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, 0) ); #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; } /* These two bridge lib/acl.c's neutral (tag,perm,id) entry array; with * HAVE_LIBACL_AT the libacl *_at path uses unpack_smb_acl/pack_smb_acl directly, * so they are unused there. */ #if defined(SUPPORT_ACL_FD) && !defined(HAVE_LIBACL_AT) /* Convert a packed system ACL into the neutral (tag,perm,id) entry array that * lib/acl.c serializes. Reuses pack_smb_acl()+change_sacl_perms() output so * the bytes we write match exactly what acl_set_file() would have written. * Returns the entry count and a malloc'd array in *ents_p, or -1 on error. */ static int sacl_to_entries(SMB_ACL_T sacl, rsync_acl_ent **ents_p) { static item_list ent_list = EMPTY_ITEM_LIST; SMB_ACL_ENTRY_T entry; rsync_acl_ent *out; int rc; ent_list.count = 0; 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; rsync_acl_ent *e; uint16_t tag; if ((rc = sys_acl_get_info(entry, &tag_type, &access, &g_u_id)) != 0)