⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 acls.c

📁 Rsync 3.0.5 source code
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * 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 + -