diff options
Diffstat (limited to 'source3/modules')
-rw-r--r-- | source3/modules/gpfs.c | 20 | ||||
-rw-r--r-- | source3/modules/nfs4_acls.c | 187 | ||||
-rw-r--r-- | source3/modules/vfs_default.c | 42 | ||||
-rw-r--r-- | source3/modules/vfs_gpfs.c | 281 | ||||
-rw-r--r-- | source3/modules/vfs_gpfs.h | 32 | ||||
-rw-r--r-- | source3/modules/vfs_prealloc.c | 8 | ||||
-rw-r--r-- | source3/modules/vfs_shadow_copy2.c | 637 | ||||
-rw-r--r-- | source3/modules/vfs_tsmsm.c | 338 |
8 files changed, 1448 insertions, 97 deletions
diff --git a/source3/modules/gpfs.c b/source3/modules/gpfs.c index 300e90fa69..590dbac26f 100644 --- a/source3/modules/gpfs.c +++ b/source3/modules/gpfs.c @@ -22,9 +22,11 @@ #ifdef HAVE_GPFS #include "gpfs_gpl.h" +#include "vfs_gpfs.h" static void *libgpfs_handle = NULL; static bool gpfs_share_modes; +static bool gpfs_leases; static int (*gpfs_set_share_fn)(int fd, unsigned int allow, unsigned int deny); static int (*gpfs_set_lease_fn)(int fd, unsigned int leaseType); @@ -42,7 +44,7 @@ bool set_gpfs_sharemode(files_struct *fsp, uint32 access_mask, if (!gpfs_share_modes) { return True; } - + if (gpfs_set_share_fn == NULL) { return False; } @@ -88,7 +90,7 @@ int set_gpfs_lease(int fd, int leasetype) { int gpfs_type = GPFS_LEASE_NONE; - if (!gpfs_share_modes) { + if (!gpfs_leases) { return True; } @@ -103,6 +105,13 @@ int set_gpfs_lease(int fd, int leasetype) if (leasetype == F_WRLCK) { gpfs_type = GPFS_LEASE_WRITE; } + + /* we unconditionally set CAP_LEASE, rather than looking for + -1/EACCES as there is a bug in some versions of + libgpfs_gpl.so which results in a leaked fd on /dev/ss0 + each time we try this with the wrong capabilities set + */ + linux_set_lease_capability(); return gpfs_set_lease_fn(fd, gpfs_type); } @@ -172,11 +181,8 @@ void init_gpfs(void) goto failed; } - if (lp_parm_bool(-1, "gpfs", "sharemodes", True)) { - gpfs_share_modes = True; - } else { - gpfs_share_modes = False; - } + gpfs_share_modes = lp_parm_bool(-1, "gpfs", "sharemodes", True); + gpfs_leases = lp_parm_bool(-1, "gpfs", "leases", True); return; diff --git a/source3/modules/nfs4_acls.c b/source3/modules/nfs4_acls.c index 52d3983fff..0c3d010dcd 100644 --- a/source3/modules/nfs4_acls.c +++ b/source3/modules/nfs4_acls.c @@ -20,6 +20,9 @@ #include "includes.h" #include "nfs4_acls.h" +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_ACLS + #define SMBACL4_PARAM_TYPE_NAME "nfs4" #define SMB_ACE4_INT_MAGIC 0x76F8A967 @@ -352,6 +355,7 @@ typedef struct _smbacl4_vfs_params { enum smbacl4_mode_enum mode; bool do_chown; enum smbacl4_acedup_enum acedup; + struct db_context *sid_mapping_table; } smbacl4_vfs_params; /* @@ -451,8 +455,65 @@ static SMB_ACE4PROP_T *smbacl4_find_equal_special( return NULL; } -static int smbacl4_fill_ace4( +static bool nfs4_map_sid(smbacl4_vfs_params *params, const DOM_SID *src, + DOM_SID *dst) +{ + static struct db_context *mapping_db = NULL; + TDB_DATA data; + + if (mapping_db == NULL) { + const char *dbname = lp_parm_const_string( + -1, SMBACL4_PARAM_TYPE_NAME, "sidmap", NULL); + + if (dbname == NULL) { + DEBUG(10, ("%s:sidmap not defined\n", + SMBACL4_PARAM_TYPE_NAME)); + return False; + } + + become_root(); + mapping_db = db_open(NULL, dbname, 0, TDB_DEFAULT, + O_RDONLY, 0600); + unbecome_root(); + + if (mapping_db == NULL) { + DEBUG(1, ("could not open sidmap: %s\n", + strerror(errno))); + return False; + } + } + + if (mapping_db->fetch(mapping_db, NULL, + string_term_tdb_data(sid_string_tos(src)), + &data) == -1) { + DEBUG(10, ("could not find mapping for SID %s\n", + sid_string_dbg(src))); + return False; + } + + if ((data.dptr == NULL) || (data.dsize <= 0) + || (data.dptr[data.dsize-1] != '\0')) { + DEBUG(5, ("invalid mapping for SID %s\n", + sid_string_dbg(src))); + TALLOC_FREE(data.dptr); + return False; + } + + if (!string_to_sid(dst, (char *)data.dptr)) { + DEBUG(1, ("invalid mapping %s for SID %s\n", + (char *)data.dptr, sid_string_dbg(src))); + TALLOC_FREE(data.dptr); + return False; + } + + TALLOC_FREE(data.dptr); + + return True; +} + +static bool smbacl4_fill_ace4( TALLOC_CTX *mem_ctx, + const char *filename, smbacl4_vfs_params *params, uid_t ownerUID, gid_t ownerGID, @@ -460,11 +521,6 @@ static int smbacl4_fill_ace4( SMB_ACE4PROP_T *ace_v4 /* output */ ) { - const char *dom, *name; - enum lsa_SidType type; - uid_t uid; - gid_t gid; - DEBUG(10, ("got ace for %s\n", sid_string_dbg(&ace_nt->trustee))); memset(ace_v4, 0, sizeof(SMB_ACE4PROP_T)); @@ -485,18 +541,46 @@ static int smbacl4_fill_ace4( ace_v4->who.special_id = SMB_ACE4_WHO_EVERYONE; ace_v4->flags |= SMB_ACE4_ID_SPECIAL; } else { - if (!lookup_sid(mem_ctx, &ace_nt->trustee, &dom, &name, &type)) { - DEBUG(8, ("Could not find %s' type\n", - sid_string_dbg(&ace_nt->trustee))); - errno = EINVAL; - return -1; + const char *dom, *name; + enum lsa_SidType type; + uid_t uid; + gid_t gid; + DOM_SID sid; + + sid_copy(&sid, &ace_nt->trustee); + + if (!lookup_sid(mem_ctx, &sid, &dom, &name, &type)) { + + DOM_SID mapped; + + if (!nfs4_map_sid(params, &sid, &mapped)) { + DEBUG(1, ("nfs4_acls.c: file [%s]: SID %s " + "unknown\n", filename, sid_string_dbg(&sid))); + errno = EINVAL; + return False; + } + + DEBUG(2, ("nfs4_acls.c: file [%s]: mapped SID %s " + "to %s\n", filename, sid_string_dbg(&sid), sid_string_dbg(&mapped))); + + if (!lookup_sid(mem_ctx, &mapped, &dom, + &name, &type)) { + DEBUG(1, ("nfs4_acls.c: file [%s]: SID %s " + "mapped from %s is unknown\n", + filename, sid_string_dbg(&mapped), sid_string_dbg(&sid))); + errno = EINVAL; + return False; + } + + sid_copy(&sid, &mapped); } - + if (type == SID_NAME_USER) { - if (!sid_to_uid(&ace_nt->trustee, &uid)) { - DEBUG(2, ("Could not convert %s to uid\n", - sid_string_dbg(&ace_nt->trustee))); - return -1; + if (!sid_to_uid(&sid, &uid)) { + DEBUG(1, ("nfs4_acls.c: file [%s]: could not " + "convert %s to uid\n", filename, + sid_string_dbg(&sid))); + return False; } if (params->mode==e_special && uid==ownerUID) { @@ -506,11 +590,13 @@ static int smbacl4_fill_ace4( ace_v4->who.uid = uid; } } else { /* else group? - TODO check it... */ - if (!sid_to_gid(&ace_nt->trustee, &gid)) { - DEBUG(2, ("Could not convert %s to gid\n", - sid_string_dbg(&ace_nt->trustee))); - return -1; + if (!sid_to_gid(&sid, &gid)) { + DEBUG(1, ("nfs4_acls.c: file [%s]: could not " + "convert %s to gid\n", filename, + sid_string_dbg(&sid))); + return False; } + ace_v4->aceFlags |= SMB_ACE4_IDENTIFIER_GROUP; if (params->mode==e_special && gid==ownerGID) { @@ -522,7 +608,7 @@ static int smbacl4_fill_ace4( } } - return 0; /* OK */ + return True; /* OK */ } static int smbacl4_MergeIgnoreReject( @@ -560,6 +646,7 @@ static int smbacl4_MergeIgnoreReject( } static SMB4ACL_T *smbacl4_win2nfs4( + const char *filename, SEC_ACL *dacl, smbacl4_vfs_params *pparams, uid_t ownerUID, @@ -580,9 +667,14 @@ static SMB4ACL_T *smbacl4_win2nfs4( SMB_ACE4PROP_T ace_v4; bool addNewACE = True; - if (smbacl4_fill_ace4(mem_ctx, pparams, ownerUID, ownerGID, - dacl->aces + i, &ace_v4)) - return NULL; + if (!smbacl4_fill_ace4(mem_ctx, filename, pparams, + ownerUID, ownerGID, + dacl->aces + i, &ace_v4)) { + DEBUG(3, ("Could not fill ace for file %s, SID %s\n", + filename, + sid_string_dbg(&((dacl->aces+i)->trustee)))); + continue; + } if (pparams->acedup!=e_dontcare) { if (smbacl4_MergeIgnoreReject(pparams->acedup, acl, @@ -607,6 +699,7 @@ NTSTATUS smb_set_nt_acl_nfs4(files_struct *fsp, bool result; SMB_STRUCT_STAT sbuf; + bool need_chown = False; uid_t newUID = (uid_t)-1; gid_t newGID = (gid_t)-1; @@ -635,25 +728,33 @@ NTSTATUS smb_set_nt_acl_nfs4(files_struct *fsp, return status; } if (((newUID != (uid_t)-1) && (sbuf.st_uid != newUID)) || - ((newGID != (gid_t)-1) && (sbuf.st_gid != newGID))) { - if(try_chown(fsp->conn, fsp->fsp_name, newUID, newGID)) { - DEBUG(3,("chown %s, %u, %u failed. Error = %s.\n", - fsp->fsp_name, (unsigned int)newUID, (unsigned int)newGID, strerror(errno) )); - if (errno == EPERM) { - return NT_STATUS_INVALID_OWNER; + ((newGID != (gid_t)-1) && (sbuf.st_gid != newGID))) { + need_chown = True; + } + if (need_chown) { + if ((newUID == (uid_t)-1 || newUID == current_user.ut.uid)) { + if(try_chown(fsp->conn, fsp->fsp_name, newUID, newGID)) { + DEBUG(3,("chown %s, %u, %u failed. Error = %s.\n", + fsp->fsp_name, (unsigned int)newUID, (unsigned int)newGID, + strerror(errno))); + return map_nt_error_from_unix(errno); } - return map_nt_error_from_unix(errno); + + DEBUG(10,("chown %s, %u, %u succeeded.\n", + fsp->fsp_name, (unsigned int)newUID, (unsigned int)newGID)); + if (smbacl4_GetFileOwner(fsp->conn, fsp->fsp_name, &sbuf)) + return map_nt_error_from_unix(errno); + need_chown = False; + } else { /* chown is needed, but _after_ changing acl */ + sbuf.st_uid = newUID; /* OWNER@ in case of e_special */ + sbuf.st_gid = newGID; /* GROUP@ in case of e_special */ } - DEBUG(10,("chown %s, %u, %u succeeded.\n", - fsp->fsp_name, (unsigned int)newUID, (unsigned int)newGID)); - if (smbacl4_fGetFileOwner(fsp, &sbuf)) - return map_nt_error_from_unix(errno); } } if ((security_info_sent & DACL_SECURITY_INFORMATION)!=0 && psd->dacl!=NULL) { - acl = smbacl4_win2nfs4(psd->dacl, ¶ms, sbuf.st_uid, sbuf.st_gid); + acl = smbacl4_win2nfs4(fsp->fsp_name, psd->dacl, ¶ms, sbuf.st_uid, sbuf.st_gid); if (!acl) return map_nt_error_from_unix(errno); @@ -668,6 +769,20 @@ NTSTATUS smb_set_nt_acl_nfs4(files_struct *fsp, } else DEBUG(10, ("no dacl found; security_info_sent = 0x%x\n", security_info_sent)); + /* Any chown pending? */ + if (need_chown) { + DEBUG(3,("chown#2 %s. uid = %u, gid = %u.\n", + fsp->fsp_name, (unsigned int)newUID, (unsigned int)newGID)); + if (try_chown(fsp->conn, fsp->fsp_name, newUID, newGID)) { + DEBUG(2,("chown#2 %s, %u, %u failed. Error = %s.\n", + fsp->fsp_name, (unsigned int)newUID, (unsigned int)newGID, + strerror(errno))); + return map_nt_error_from_unix(errno); + } + DEBUG(10,("chown#2 %s, %u, %u succeeded.\n", + fsp->fsp_name, (unsigned int)newUID, (unsigned int)newGID)); + } + DEBUG(10, ("smb_set_nt_acl_nfs4 succeeded\n")); return NT_STATUS_OK; } diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c index e21136ccee..3e78c69a5e 100644 --- a/source3/modules/vfs_default.c +++ b/source3/modules/vfs_default.c @@ -1225,6 +1225,38 @@ static int vfswrap_aio_suspend(struct vfs_handle_struct *handle, struct files_st return sys_aio_suspend(aiocb, n, timeout); } +static int vfswrap_aio_force(struct vfs_handle_struct *handle, struct files_struct *fsp) +{ + return False; +} + +static int vfswrap_is_offline(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, bool *offline) +{ + if (ISDOT(path) || ISDOTDOT(path)) { + return 1; + } + + if (!lp_dmapi_support(SNUM(conn)) || !dmapi_have_session()) { + return -1; + } + + *offline = (dmapi_file_flags(path) & FILE_ATTRIBUTE_OFFLINE) != 0; + return 0; +} + +static int vfswrap_set_offline(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path) +{ + /* We don't know how to set offline bit by default, needs to be overriden in the vfs modules */ + return -1; +} + +static bool vfswrap_is_remotestorage(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path) +{ + /* We don't know how to detect that volume is remote storage. VFS modules should redefine it. */ + return False; +} + + static vfs_op_tuple vfs_default_ops[] = { /* Disk operations */ @@ -1442,6 +1474,16 @@ static vfs_op_tuple vfs_default_ops[] = { {SMB_VFS_OP(vfswrap_aio_suspend),SMB_VFS_OP_AIO_SUSPEND, SMB_VFS_LAYER_OPAQUE}, + {SMB_VFS_OP(vfswrap_aio_force), SMB_VFS_OP_AIO_FORCE, + SMB_VFS_LAYER_OPAQUE}, + + {SMB_VFS_OP(vfswrap_is_offline),SMB_VFS_OP_IS_OFFLINE, + SMB_VFS_LAYER_OPAQUE}, + {SMB_VFS_OP(vfswrap_set_offline),SMB_VFS_OP_SET_OFFLINE, + SMB_VFS_LAYER_OPAQUE}, + {SMB_VFS_OP(vfswrap_is_remotestorage),SMB_VFS_OP_IS_REMOTESTORAGE, + SMB_VFS_LAYER_OPAQUE}, + /* Finish VFS operations definition */ {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, diff --git a/source3/modules/vfs_gpfs.c b/source3/modules/vfs_gpfs.c index bcf61f3bc7..d10906dfb1 100644 --- a/source3/modules/vfs_gpfs.c +++ b/source3/modules/vfs_gpfs.c @@ -30,7 +30,7 @@ #include <gpfs_gpl.h> #include "nfs4_acls.h" - +#include "vfs_gpfs.h" static int vfs_gpfs_kernel_flock(vfs_handle_struct *handle, files_struct *fsp, uint32 share_mode) @@ -153,7 +153,7 @@ static int gpfs_get_nfs4_acl(const char *fname, SMB4ACL_T **ppacl) DEBUG(10, ("gpfs_get_nfs4_acl invoked for %s\n", fname)); /* First get the real acl length */ - gacl = gpfs_getacl_alloc(fname, GPFS_ACL_TYPE_NFS4); + gacl = gpfs_getacl_alloc(fname, 0); if (gacl == NULL) { DEBUG(9, ("gpfs_getacl failed for %s with %s\n", fname, strerror(errno))); @@ -208,10 +208,10 @@ static int gpfs_get_nfs4_acl(const char *fname, SMB4ACL_T **ppacl) if (i > 0 && gace->aceType == SMB_ACE4_ACCESS_DENIED_ACE_TYPE) { struct gpfs_ace_v4 *prev = &gacl->ace_v4[i-1]; if (prev->aceType == SMB_ACE4_ACCESS_ALLOWED_ACE_TYPE && - prev->aceFlags == gace->aceFlags && - prev->aceIFlags == gace->aceIFlags && - (gace->aceMask & prev->aceMask) == 0 && - gace->aceWho == prev->aceWho) { + prev->aceFlags == gace->aceFlags && + prev->aceIFlags == gace->aceIFlags && + (gace->aceMask & prev->aceMask) == 0 && + gace->aceWho == prev->aceWho) { /* its redundent - skip it */ continue; } @@ -256,7 +256,7 @@ static NTSTATUS gpfsacl_get_nt_acl(vfs_handle_struct *handle, int result; *ppdesc = NULL; - result = gpfs_get_nfs4_acl(fsp->fsp_name, &pacl); + result = gpfs_get_nfs4_acl(name, &pacl); if (result == 0) return smb_get_nt_acl_nfs4(handle->conn, name, security_info, ppdesc, pacl); @@ -301,8 +301,31 @@ static bool gpfsacl_process_smbacl(files_struct *fsp, SMB4ACL_T *smbacl) gace->aceType = aceprop->aceType; gace->aceFlags = aceprop->aceFlags; gace->aceMask = aceprop->aceMask; + + /* + * GPFS can't distinguish between WRITE and APPEND on + * files, so one being set without the other is an + * error. Sorry for the many ()'s :-) + */ + + if (!fsp->is_directory + && + ((((gace->aceMask & ACE4_MASK_WRITE) == 0) + && ((gace->aceMask & ACE4_MASK_APPEND) != 0)) + || + (((gace->aceMask & ACE4_MASK_WRITE) != 0) + && ((gace->aceMask & ACE4_MASK_APPEND) == 0))) + && + lp_parm_bool(fsp->conn->params->service, "gpfs", + "merge_writeappend", True)) { + DEBUG(2, ("vfs_gpfs.c: file [%s]: ACE contains " + "WRITE^APPEND, setting WRITE|APPEND\n", + fsp->fsp_name)); + gace->aceMask |= ACE4_MASK_WRITE|ACE4_MASK_APPEND; + } + gace->aceIFlags = (aceprop->flags&SMB_ACE4_ID_SPECIAL) ? ACE4_IFLAG_SPECIAL_ID : 0; - + if (aceprop->flags&SMB_ACE4_ID_SPECIAL) { switch(aceprop->who.special_id) @@ -347,7 +370,7 @@ static NTSTATUS gpfsacl_set_nt_acl_internal(files_struct *fsp, uint32 security_i struct gpfs_acl *acl; NTSTATUS result = NT_STATUS_ACCESS_DENIED; - acl = gpfs_getacl_alloc(fsp->fsp_name, GPFS_ACL_TYPE_ACCESS); + acl = gpfs_getacl_alloc(fsp->fsp_name, 0); if (acl == NULL) return result; @@ -628,75 +651,225 @@ int gpfsacl_sys_acl_delete_def_file(vfs_handle_struct *handle, return -1; } +/* + * Assumed: mode bits are shiftable and standard + * Output: the new aceMask field for an smb nfs4 ace + */ +static uint32 gpfsacl_mask_filter(uint32 aceType, uint32 aceMask, uint32 rwx) +{ + const uint32 posix_nfs4map[3] = { + SMB_ACE4_EXECUTE, /* execute */ + SMB_ACE4_WRITE_DATA | SMB_ACE4_APPEND_DATA, /* write; GPFS specific */ + SMB_ACE4_READ_DATA /* read */ + }; + int i; + uint32_t posix_mask = 0x01; + uint32_t posix_bit; + uint32_t nfs4_bits; + + for(i=0; i<3; i++) { + nfs4_bits = posix_nfs4map[i]; + posix_bit = rwx & posix_mask; + + if (aceType==SMB_ACE4_ACCESS_ALLOWED_ACE_TYPE) { + if (posix_bit) + aceMask |= nfs4_bits; + else + aceMask &= ~nfs4_bits; + } else { + /* add deny bits when suitable */ + if (!posix_bit) + aceMask |= nfs4_bits; + else + aceMask &= ~nfs4_bits; + } /* other ace types are unexpected */ + + posix_mask <<= 1; + } + + return aceMask; +} + +static int gpfsacl_emu_chmod(const char *path, mode_t mode) +{ + SMB4ACL_T *pacl = NULL; + int result; + bool haveAllowEntry[SMB_ACE4_WHO_EVERYONE + 1] = {False, False, False, False}; + int i; + files_struct fake_fsp; /* TODO: rationalize parametrization */ + SMB4ACE_T *smbace; + + DEBUG(10, ("gpfsacl_emu_chmod invoked for %s mode %o\n", path, mode)); + + result = gpfs_get_nfs4_acl(path, &pacl); + if (result) + return result; + + if (mode & ~(S_IRWXU | S_IRWXG | S_IRWXO)) { + DEBUG(2, ("WARNING: cutting extra mode bits %o on %s\n", mode, path)); + } + + for (smbace=smb_first_ace4(pacl); smbace!=NULL; smbace = smb_next_ace4(smbace)) { + SMB_ACE4PROP_T *ace = smb_get_ace4(smbace); + uint32_t specid = ace->who.special_id; + + if (ace->flags&SMB_ACE4_ID_SPECIAL && + ace->aceType<=SMB_ACE4_ACCESS_DENIED_ACE_TYPE && + specid <= SMB_ACE4_WHO_EVERYONE) { + + uint32_t newMask; + + if (ace->aceType==SMB_ACE4_ACCESS_ALLOWED_ACE_TYPE) + haveAllowEntry[specid] = True; + + /* mode >> 6 for @owner, mode >> 3 for @group, + * mode >> 0 for @everyone */ + newMask = gpfsacl_mask_filter(ace->aceType, ace->aceMask, + mode >> ((SMB_ACE4_WHO_EVERYONE - specid) * 3)); + if (ace->aceMask!=newMask) { + DEBUG(10, ("ace changed for %s (%o -> %o) id=%d\n", + path, ace->aceMask, newMask, specid)); + } + ace->aceMask = newMask; + } + } + + /* make sure we have at least ALLOW entries + * for all the 3 special ids (@EVERYONE, @OWNER, @GROUP) + * - if necessary + */ + for(i = SMB_ACE4_WHO_OWNER; i<=SMB_ACE4_WHO_EVERYONE; i++) { + SMB_ACE4PROP_T ace; + + if (haveAllowEntry[i]==True) + continue; + + memset(&ace, 0, sizeof(SMB_ACE4PROP_T)); + ace.aceType = SMB_ACE4_ACCESS_ALLOWED_ACE_TYPE; + ace.flags |= SMB_ACE4_ID_SPECIAL; + ace.who.special_id = i; + + if (i==SMB_ACE4_WHO_GROUP) /* not sure it's necessary... */ + ace.aceFlags |= SMB_ACE4_IDENTIFIER_GROUP; + + ace.aceMask = gpfsacl_mask_filter(ace.aceType, ace.aceMask, + mode >> ((SMB_ACE4_WHO_EVERYONE - i) * 3)); + + /* don't add unnecessary aces */ + if (!ace.aceMask) + continue; + + /* we add it to the END - as windows expects allow aces */ + smb_add_ace4(pacl, &ace); + DEBUG(10, ("Added ALLOW ace for %s, mode=%o, id=%d, aceMask=%x\n", + path, mode, i, ace.aceMask)); + } + + /* don't add complementary DENY ACEs here */ + memset(&fake_fsp, 0, sizeof(struct files_struct)); + fake_fsp.fsp_name = (char *)path; /* no file_new is needed here */ + + /* put the acl */ + if (gpfsacl_process_smbacl(&fake_fsp, pacl) == False) + return -1; + return 0; /* ok for [f]chmod */ +} + static int vfs_gpfs_chmod(vfs_handle_struct *handle, const char *path, mode_t mode) { SMB_STRUCT_STAT st; + int rc; + if (SMB_VFS_NEXT_STAT(handle, path, &st) != 0) { - return -1; + return -1; } + /* avoid chmod() if possible, to preserve acls */ if ((st.st_mode & ~S_IFMT) == mode) { - return 0; + return 0; } - return SMB_VFS_NEXT_CHMOD(handle, path, mode); + + rc = gpfsacl_emu_chmod(path, mode); + if (rc == 1) + return SMB_VFS_NEXT_CHMOD(handle, path, mode); + return rc; } static int vfs_gpfs_fchmod(vfs_handle_struct *handle, files_struct *fsp, mode_t mode) { SMB_STRUCT_STAT st; + int rc; + if (SMB_VFS_NEXT_FSTAT(handle, fsp, &st) != 0) { - return -1; + return -1; } + /* avoid chmod() if possible, to preserve acls */ if ((st.st_mode & ~S_IFMT) == mode) { - return 0; + return 0; } - return SMB_VFS_NEXT_FCHMOD(handle, fsp, mode); + + rc = gpfsacl_emu_chmod(fsp->fsp_name, mode); + if (rc == 1) + return SMB_VFS_NEXT_FCHMOD(handle, fsp, mode); + return rc; } /* VFS operations structure */ static vfs_op_tuple gpfs_op_tuples[] = { - - { SMB_VFS_OP(vfs_gpfs_kernel_flock), SMB_VFS_OP_KERNEL_FLOCK, - SMB_VFS_LAYER_OPAQUE }, - - { SMB_VFS_OP(vfs_gpfs_setlease), SMB_VFS_OP_LINUX_SETLEASE, - SMB_VFS_LAYER_OPAQUE }, - - { SMB_VFS_OP(gpfsacl_fget_nt_acl), SMB_VFS_OP_FGET_NT_ACL, - SMB_VFS_LAYER_TRANSPARENT }, - - { SMB_VFS_OP(gpfsacl_get_nt_acl), SMB_VFS_OP_GET_NT_ACL, - SMB_VFS_LAYER_TRANSPARENT }, - - { SMB_VFS_OP(gpfsacl_fset_nt_acl), SMB_VFS_OP_FSET_NT_ACL, - SMB_VFS_LAYER_TRANSPARENT }, - - { SMB_VFS_OP(gpfsacl_set_nt_acl), SMB_VFS_OP_SET_NT_ACL, - SMB_VFS_LAYER_TRANSPARENT }, - - { SMB_VFS_OP(gpfsacl_sys_acl_get_file), SMB_VFS_OP_SYS_ACL_GET_FILE, - SMB_VFS_LAYER_TRANSPARENT }, - - { SMB_VFS_OP(gpfsacl_sys_acl_get_fd), SMB_VFS_OP_SYS_ACL_GET_FD, - SMB_VFS_LAYER_TRANSPARENT }, - - { SMB_VFS_OP(gpfsacl_sys_acl_set_file), SMB_VFS_OP_SYS_ACL_SET_FILE, - SMB_VFS_LAYER_TRANSPARENT }, - - { SMB_VFS_OP(gpfsacl_sys_acl_set_fd), SMB_VFS_OP_SYS_ACL_SET_FD, - SMB_VFS_LAYER_TRANSPARENT }, - + + { SMB_VFS_OP(vfs_gpfs_kernel_flock), + SMB_VFS_OP_KERNEL_FLOCK, + SMB_VFS_LAYER_OPAQUE }, + + { SMB_VFS_OP(vfs_gpfs_setlease), + SMB_VFS_OP_LINUX_SETLEASE, + SMB_VFS_LAYER_OPAQUE }, + + { SMB_VFS_OP(gpfsacl_fget_nt_acl), + SMB_VFS_OP_FGET_NT_ACL, + SMB_VFS_LAYER_TRANSPARENT }, + + { SMB_VFS_OP(gpfsacl_get_nt_acl), + SMB_VFS_OP_GET_NT_ACL, + SMB_VFS_LAYER_TRANSPARENT }, + + { SMB_VFS_OP(gpfsacl_fset_nt_acl), + SMB_VFS_OP_FSET_NT_ACL, + SMB_VFS_LAYER_TRANSPARENT }, + + { SMB_VFS_OP(gpfsacl_set_nt_acl), + SMB_VFS_OP_SET_NT_ACL, + SMB_VFS_LAYER_TRANSPARENT }, + + { SMB_VFS_OP(gpfsacl_sys_acl_get_file), + SMB_VFS_OP_SYS_ACL_GET_FILE, + SMB_VFS_LAYER_TRANSPARENT }, + + { SMB_VFS_OP(gpfsacl_sys_acl_get_fd), + SMB_VFS_OP_SYS_ACL_GET_FD, + SMB_VFS_LAYER_TRANSPARENT }, + + { SMB_VFS_OP(gpfsacl_sys_acl_set_file), + SMB_VFS_OP_SYS_ACL_SET_FILE, + SMB_VFS_LAYER_TRANSPARENT }, + + { SMB_VFS_OP(gpfsacl_sys_acl_set_fd), + SMB_VFS_OP_SYS_ACL_SET_FD, + SMB_VFS_LAYER_TRANSPARENT }, + { SMB_VFS_OP(gpfsacl_sys_acl_delete_def_file), - SMB_VFS_OP_SYS_ACL_DELETE_DEF_FILE, - SMB_VFS_LAYER_TRANSPARENT }, - - { SMB_VFS_OP(vfs_gpfs_chmod), SMB_VFS_OP_CHMOD, - SMB_VFS_LAYER_TRANSPARENT }, - - { SMB_VFS_OP(vfs_gpfs_fchmod), SMB_VFS_OP_FCHMOD, - SMB_VFS_LAYER_TRANSPARENT }, + SMB_VFS_OP_SYS_ACL_DELETE_DEF_FILE, + SMB_VFS_LAYER_TRANSPARENT }, + + { SMB_VFS_OP(vfs_gpfs_chmod), + SMB_VFS_OP_CHMOD, + SMB_VFS_LAYER_TRANSPARENT }, + + { SMB_VFS_OP(vfs_gpfs_fchmod), + SMB_VFS_OP_FCHMOD, + SMB_VFS_LAYER_TRANSPARENT }, { SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP } diff --git a/source3/modules/vfs_gpfs.h b/source3/modules/vfs_gpfs.h new file mode 100644 index 0000000000..3c499b0850 --- /dev/null +++ b/source3/modules/vfs_gpfs.h @@ -0,0 +1,32 @@ +/* + Unix SMB/CIFS implementation. + Wrap gpfs calls in vfs functions. + + Copyright (C) Christian Ambach <cambach1@de.ibm.com> 2006 + + Major code contributions by Chetan Shringarpure <chetan.sh@in.ibm.com> + and Gomati Mohanan <gomati.mohanan@in.ibm.com> + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + +*/ + +bool set_gpfs_sharemode(files_struct *fsp, uint32 access_mask, + uint32 share_access); +int set_gpfs_lease(int fd, int leasetype); +int smbd_gpfs_getacl(char *pathname, int flags, void *acl); +int smbd_gpfs_putacl(char *pathname, int flags, void *acl); +void init_gpfs(void); diff --git a/source3/modules/vfs_prealloc.c b/source3/modules/vfs_prealloc.c index 2d64bc0184..cb3508dc30 100644 --- a/source3/modules/vfs_prealloc.c +++ b/source3/modules/vfs_prealloc.c @@ -47,11 +47,16 @@ #define lock_type struct flock64 #endif +#ifdef HAVE_GPFS +#include "gpfs_gpl.h" +#endif + #define MODULE "prealloc" static int module_debug; static int preallocate_space(int fd, SMB_OFF_T size) { +#ifndef HAVE_GPFS lock_type fl = {0}; int err; @@ -78,6 +83,9 @@ static int preallocate_space(int fd, SMB_OFF_T size) err = -1; errno = ENOSYS; #endif +#else /* GPFS uses completely different interface */ + err = gpfs_prealloc(fd, (gpfs_off64_t)0, (gpfs_off64_t)size); +#endif if (err) { DEBUG(module_debug, diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c new file mode 100644 index 0000000000..54fc672b9c --- /dev/null +++ b/source3/modules/vfs_shadow_copy2.c @@ -0,0 +1,637 @@ +/* + * implementation of an Shadow Copy module - version 2 + * + * Copyright (C) Andrew Tridgell 2007 + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "includes.h" + +/* + + This is a 2nd implemetation of a shadow copy module for exposing + snapshots to windows clients as shadow copies. This version has the + following features: + + 1) you don't need to populate your shares with symlinks to the + snapshots. This can be very important when you have thousands of + shares, or use [homes] + + 2) the inode number of the files is altered so it is different + from the original. This allows the 'restore' button to work + without a sharing violation + + Module options: + + shadow:snapdir = <directory where snapshots are kept> + + This is the directory containing the @GMT-* snapshot directories. If it is an absolute + path it is used as-is. If it is a relative path, then it is taken relative to the mount + point of the filesystem that the root of this share is on + + shadow:basedir = <base directory that snapshots are from> + + This is an optional parameter that specifies the directory that + the snapshots are relative to. It defaults to the filesystem + mount point + + shadow:fixinodes = yes/no + + If you enable shadow:fixinodes then this module will modify the + apparent inode number of files in the snapshot directories using + a hash of the files path. This is needed for snapshot systems + where the snapshots have the same device:inode number as the + original files (such as happens with GPFS snapshots). If you + don't set this option then the 'restore' button in the shadow + copy UI will fail with a sharing violation. + + Note that the directory names in the snapshot directory must take the form + @GMT-YYYY.MM.DD-HH.MM.SS + + The following command would generate a correctly formatted directory name: + date -u +@GMT-%Y.%m.%d-%H.%M.%S + + */ + +static int vfs_shadow_copy2_debug_level = DBGC_VFS; + +#undef DBGC_CLASS +#define DBGC_CLASS vfs_shadow_copy2_debug_level + +#define GMT_NAME_LEN 24 /* length of a @GMT- name */ + +/* + make very sure it is one of our special names + */ +static inline bool shadow_copy2_match_name(const char *name) +{ + unsigned year, month, day, hr, min, sec; + if (name[0] != '@') return False; + if (strncmp(name, "@GMT-", 5) != 0) return False; + if (sscanf(name, "@GMT-%04u.%02u.%02u-%02u.%02u.%02u", &year, &month, + &day, &hr, &min, &sec) != 6) { + return False; + } + if (name[24] != 0 && name[24] != '/') { + return False; + } + return True; +} + +/* + convert a name to the shadow directory + */ + +#define _SHADOW2_NEXT(op, args, rtype, eret, extra) do { \ + const char *name = fname; \ + if (shadow_copy2_match_name(fname)) { \ + char *name2; \ + rtype ret; \ + name2 = convert_shadow2_name(handle, fname); \ + if (name2 == NULL) { \ + errno = EINVAL; \ + return eret; \ + } \ + name = name2; \ + ret = SMB_VFS_NEXT_ ## op args; \ + talloc_free(name2); \ + if (ret != eret) extra; \ + return ret; \ + } else { \ + return SMB_VFS_NEXT_ ## op args; \ + } \ +} while (0) + +/* + convert a name to the shadow directory: NTSTATUS-specific handling + */ + +#define _SHADOW2_NTSTATUS_NEXT(op, args, eret, extra) do { \ + const char *name = fname; \ + if (shadow_copy2_match_name(fname)) { \ + char *name2; \ + NTSTATUS ret; \ + name2 = convert_shadow2_name(handle, fname); \ + if (name2 == NULL) { \ + errno = EINVAL; \ + return eret; \ + } \ + name = name2; \ + ret = SMB_VFS_NEXT_ ## op args; \ + talloc_free(name2); \ + if (!NT_STATUS_EQUAL(ret, eret)) extra; \ + return ret; \ + } else { \ + return SMB_VFS_NEXT_ ## op args; \ + } \ +} while (0) + +#define SHADOW2_NTSTATUS_NEXT(op, args, eret) _SHADOW2_NTSTATUS_NEXT(op, args, eret, ) + +#define SHADOW2_NEXT(op, args, rtype, eret) _SHADOW2_NEXT(op, args, rtype, eret, ) + +#define SHADOW2_NEXT2(op, args) do { \ + if (shadow_copy2_match_name(oldname) || shadow_copy2_match_name(newname)) { \ + errno = EROFS; \ + return -1; \ + } else { \ + return SMB_VFS_NEXT_ ## op args; \ + } \ +} while (0) + + +/* + find the mount point of a filesystem + */ +static char *find_mount_point(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle) +{ + char *path = talloc_strdup(mem_ctx, handle->conn->connectpath); + dev_t dev; + struct stat st; + char *p; + + if (stat(path, &st) != 0) { + talloc_free(path); + return NULL; + } + + dev = st.st_dev; + + while ((p = strrchr(path, '/')) && p > path) { + *p = 0; + if (stat(path, &st) != 0) { + talloc_free(path); + return NULL; + } + if (st.st_dev != dev) { + *p = '/'; + break; + } + } + + return path; +} + +/* + work out the location of the snapshot for this share + */ +static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle) +{ + const char *snapdir; + char *mount_point; + const char *ret; + + snapdir = lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir", NULL); + if (snapdir == NULL) { + return NULL; + } + /* if its an absolute path, we're done */ + if (*snapdir == '/') { + return snapdir; + } + + /* other its relative to the filesystem mount point */ + mount_point = find_mount_point(mem_ctx, handle); + if (mount_point == NULL) { + return NULL; + } + + ret = talloc_asprintf(mem_ctx, "%s/%s", mount_point, snapdir); + talloc_free(mount_point); + return ret; +} + +/* + work out the location of the base directory for snapshots of this share + */ +static const char *shadow_copy2_find_basedir(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle) +{ + const char *basedir = lp_parm_const_string(SNUM(handle->conn), "shadow", "basedir", NULL); + + /* other its the filesystem mount point */ + if (basedir == NULL) { + basedir = find_mount_point(mem_ctx, handle); + } + + return basedir; +} + +/* + convert a filename from a share relative path, to a path in the + snapshot directory + */ +static char *convert_shadow2_name(vfs_handle_struct *handle, const char *fname) +{ + TALLOC_CTX *tmp_ctx = talloc_new(handle->data); + const char *snapdir, *relpath, *baseoffset, *basedir; + size_t baselen; + char *ret; + + snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle); + if (snapdir == NULL) { + DEBUG(2,("no snapdir found for share at %s\n", handle->conn->connectpath)); + talloc_free(tmp_ctx); + return NULL; + } + + basedir = shadow_copy2_find_basedir(tmp_ctx, handle); + if (basedir == NULL) { + DEBUG(2,("no basedir found for share at %s\n", handle->conn->connectpath)); + talloc_free(tmp_ctx); + return NULL; + } + + relpath = fname + GMT_NAME_LEN; + baselen = strlen(basedir); + baseoffset = handle->conn->connectpath + baselen; + + /* some sanity checks */ + if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 || + (handle->conn->connectpath[baselen] != 0 && handle->conn->connectpath[baselen] != '/')) { + DEBUG(0,("convert_shadow2_name: basedir %s is not a parent of %s\n", + basedir, handle->conn->connectpath)); + talloc_free(tmp_ctx); + return NULL; + } + + if (*relpath == '/') relpath++; + if (*baseoffset == '/') baseoffset++; + + ret = talloc_asprintf(handle->data, "%s/%.*s/%s/%s", + snapdir, + GMT_NAME_LEN, fname, + baseoffset, + relpath); + DEBUG(6,("convert_shadow2_name: '%s' -> '%s'\n", fname, ret)); + talloc_free(tmp_ctx); + return ret; +} + + +/* + simple string hash + */ +static uint32 string_hash(const char *s) +{ + uint32 n = 0; + while (*s) { + n = ((n << 5) + n) ^ (uint32)(*s++); + } + return n; +} + +/* + modify a sbuf return to ensure that inodes in the shadow directory + are different from those in the main directory + */ +static void convert_sbuf(vfs_handle_struct *handle, const char *fname, SMB_STRUCT_STAT *sbuf) +{ + if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) { + /* some snapshot systems, like GPFS, return the name + device:inode for the snapshot files as the current + files. That breaks the 'restore' button in the shadow copy + GUI, as the client gets a sharing violation. + + This is a crude way of allowing both files to be + open at once. It has a slight chance of inode + number collision, but I can't see a better approach + without significant VFS changes + */ + uint32_t shash = string_hash(fname) & 0xFF000000; + if (shash == 0) { + shash = 1; + } + sbuf->st_ino ^= shash; + } +} + +static int shadow_copy2_rename(vfs_handle_struct *handle, + const char *oldname, const char *newname) +{ + SHADOW2_NEXT2(RENAME, (handle, oldname, newname)); +} + +static int shadow_copy2_symlink(vfs_handle_struct *handle, + const char *oldname, const char *newname) +{ + SHADOW2_NEXT2(SYMLINK, (handle, oldname, newname)); +} + +static int shadow_copy2_link(vfs_handle_struct *handle, + const char *oldname, const char *newname) +{ + SHADOW2_NEXT2(LINK, (handle, oldname, newname)); +} + +static int shadow_copy2_open(vfs_handle_struct *handle, + const char *fname, files_struct *fsp, int flags, mode_t mode) +{ + SHADOW2_NEXT(OPEN, (handle, name, fsp, flags, mode), int, -1); +} + +static SMB_STRUCT_DIR *shadow_copy2_opendir(vfs_handle_struct *handle, + const char *fname, const char *mask, uint32 attr) +{ + SHADOW2_NEXT(OPENDIR, (handle, name, mask, attr), void*, NULL); +} + +static int shadow_copy2_stat(vfs_handle_struct *handle, + const char *fname, SMB_STRUCT_STAT *sbuf) +{ + _SHADOW2_NEXT(STAT, (handle, name, sbuf), int, -1, convert_sbuf(handle, fname, sbuf)); +} + +static int shadow_copy2_lstat(vfs_handle_struct *handle, + const char *fname, SMB_STRUCT_STAT *sbuf) +{ + _SHADOW2_NEXT(LSTAT, (handle, name, sbuf), int, -1, convert_sbuf(handle, fname, sbuf)); +} + +static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf) +{ + int ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf); + if (ret == 0 && shadow_copy2_match_name(fsp->fsp_name)) { + convert_sbuf(handle, fsp->fsp_name, sbuf); + } + return ret; +} + +static int shadow_copy2_unlink(vfs_handle_struct *handle, const char *fname) +{ + SHADOW2_NEXT(UNLINK, (handle, name), int, -1); +} + +static int shadow_copy2_chmod(vfs_handle_struct *handle, + const char *fname, mode_t mode) +{ + SHADOW2_NEXT(CHMOD, (handle, name, mode), int, -1); +} + +static int shadow_copy2_chown(vfs_handle_struct *handle, + const char *fname, uid_t uid, gid_t gid) +{ + SHADOW2_NEXT(CHOWN, (handle, name, uid, gid), int, -1); +} + +static int shadow_copy2_chdir(vfs_handle_struct *handle, + const char *fname) +{ + SHADOW2_NEXT(CHDIR, (handle, name), int, -1); +} + +static int shadow_copy2_ntimes(vfs_handle_struct *handle, + const char *fname, const struct timespec ts[2]) +{ + SHADOW2_NEXT(NTIMES, (handle, name, ts), int, -1); +} + +static int shadow_copy2_readlink(vfs_handle_struct *handle, + const char *fname, char *buf, size_t bufsiz) +{ + SHADOW2_NEXT(READLINK, (handle, name, buf, bufsiz), int, -1); +} + +static int shadow_copy2_mknod(vfs_handle_struct *handle, + const char *fname, mode_t mode, SMB_DEV_T dev) +{ + SHADOW2_NEXT(MKNOD, (handle, name, mode, dev), int, -1); +} + +static char *shadow_copy2_realpath(vfs_handle_struct *handle, + const char *fname, char *resolved_path) +{ + SHADOW2_NEXT(REALPATH, (handle, name, resolved_path), void*, NULL); +} + +static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle, + const char *fname, uint32 security_info, + struct security_descriptor **ppdesc) +{ + SHADOW2_NTSTATUS_NEXT(GET_NT_ACL, (handle, name, security_info, ppdesc), NT_STATUS_ACCESS_DENIED); +} + +static NTSTATUS shadow_copy2_set_nt_acl(vfs_handle_struct *handle, files_struct *fsp, + const char *fname, uint32 security_info_sent, + struct security_descriptor *psd) +{ + SHADOW2_NTSTATUS_NEXT(SET_NT_ACL, (handle, fsp, name, security_info_sent, psd), NT_STATUS_ACCESS_DENIED); +} + +static int shadow_copy2_mkdir(vfs_handle_struct *handle, const char *fname, mode_t mode) +{ + SHADOW2_NEXT(MKDIR, (handle, name, mode), int, -1); +} + +static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname) +{ + SHADOW2_NEXT(RMDIR, (handle, name), int, -1); +} + +static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname, int flags) +{ + SHADOW2_NEXT(CHFLAGS, (handle, name, flags), int, -1); +} + +static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle, + const char *fname, const char *aname, void *value, size_t size) +{ + SHADOW2_NEXT(GETXATTR, (handle, name, aname, value, size), ssize_t, -1); +} + +static ssize_t shadow_copy2_lgetxattr(vfs_handle_struct *handle, + const char *fname, const char *aname, void *value, size_t size) +{ + SHADOW2_NEXT(LGETXATTR, (handle, name, aname, value, size), ssize_t, -1); +} + +static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle, const char *fname, + char *list, size_t size) +{ + SHADOW2_NEXT(LISTXATTR, (handle, name, list, size), ssize_t, -1); +} + +static int shadow_copy2_removexattr(struct vfs_handle_struct *handle, const char *fname, + const char *aname) +{ + SHADOW2_NEXT(REMOVEXATTR, (handle, name, aname), int, -1); +} + +static int shadow_copy2_lremovexattr(struct vfs_handle_struct *handle, const char *fname, + const char *aname) +{ + SHADOW2_NEXT(LREMOVEXATTR, (handle, name, aname), int, -1); +} + +static int shadow_copy2_setxattr(struct vfs_handle_struct *handle, const char *fname, + const char *aname, const void *value, size_t size, int flags) +{ + SHADOW2_NEXT(SETXATTR, (handle, name, aname, value, size, flags), int, -1); +} + +static int shadow_copy2_lsetxattr(struct vfs_handle_struct *handle, const char *fname, + const char *aname, const void *value, size_t size, int flags) +{ + SHADOW2_NEXT(LSETXATTR, (handle, name, aname, value, size, flags), int, -1); +} + +static int shadow_copy2_chmod_acl(vfs_handle_struct *handle, + const char *fname, mode_t mode) +{ + /* If the underlying VFS doesn't have ACL support... */ + if (!handle->vfs_next.ops.chmod_acl) { + errno = ENOSYS; + return -1; + } + SHADOW2_NEXT(CHMOD_ACL, (handle, name, mode), int, -1); +} + +static int shadow_copy2_get_shadow_copy2_data(vfs_handle_struct *handle, + files_struct *fsp, + SHADOW_COPY_DATA *shadow_copy2_data, + bool labels) +{ + SMB_STRUCT_DIR *p; + const char *snapdir; + SMB_STRUCT_DIRENT *d; + TALLOC_CTX *tmp_ctx = talloc_new(handle->data); + + snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle); + if (snapdir == NULL) { + DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n", + handle->conn->connectpath)); + errno = EINVAL; + talloc_free(tmp_ctx); + return -1; + } + + p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0); + + if (!p) { + DEBUG(0,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s' - %s\n", + snapdir, strerror(errno))); + talloc_free(tmp_ctx); + return -1; + } + + talloc_free(tmp_ctx); + + shadow_copy2_data->num_volumes = 0; + shadow_copy2_data->labels = NULL; + + while ((d = SMB_VFS_NEXT_READDIR(handle, p))) { + SHADOW_COPY_LABEL *tlabels; + + /* ignore names not of the right form in the snapshot directory */ + if (!shadow_copy2_match_name(d->d_name)) { + continue; + } + + if (!labels) { + /* the caller doesn't want the labels */ + shadow_copy2_data->num_volumes++; + continue; + } + + tlabels = talloc_realloc(shadow_copy2_data->mem_ctx, + shadow_copy2_data->labels, + SHADOW_COPY_LABEL, shadow_copy2_data->num_volumes+1); + if (tlabels == NULL) { + DEBUG(0,("shadow_copy2: out of memory\n")); + SMB_VFS_NEXT_CLOSEDIR(handle, p); + return -1; + } + + strlcpy(tlabels[shadow_copy2_data->num_volumes], d->d_name, sizeof(*tlabels)); + shadow_copy2_data->num_volumes++; + shadow_copy2_data->labels = tlabels; + } + + SMB_VFS_NEXT_CLOSEDIR(handle,p); + return 0; +} + +/* VFS operations structure */ + +static vfs_op_tuple shadow_copy2_ops[] = { + {SMB_VFS_OP(shadow_copy2_opendir), SMB_VFS_OP_OPENDIR, SMB_VFS_LAYER_TRANSPARENT}, + + /* directory operations */ + {SMB_VFS_OP(shadow_copy2_mkdir), SMB_VFS_OP_MKDIR, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(shadow_copy2_rmdir), SMB_VFS_OP_RMDIR, SMB_VFS_LAYER_TRANSPARENT}, + + /* xattr and flags operations */ + {SMB_VFS_OP(shadow_copy2_chflags), SMB_VFS_OP_CHFLAGS, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(shadow_copy2_getxattr), SMB_VFS_OP_GETXATTR, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(shadow_copy2_lgetxattr), SMB_VFS_OP_LGETXATTR, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(shadow_copy2_listxattr), SMB_VFS_OP_LISTXATTR, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(shadow_copy2_removexattr), SMB_VFS_OP_REMOVEXATTR, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(shadow_copy2_lremovexattr),SMB_VFS_OP_LREMOVEXATTR,SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(shadow_copy2_setxattr), SMB_VFS_OP_SETXATTR, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(shadow_copy2_lsetxattr), SMB_VFS_OP_LSETXATTR, SMB_VFS_LAYER_TRANSPARENT}, + + /* File operations */ + {SMB_VFS_OP(shadow_copy2_open), SMB_VFS_OP_OPEN, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(shadow_copy2_rename), SMB_VFS_OP_RENAME, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(shadow_copy2_stat), SMB_VFS_OP_STAT, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(shadow_copy2_lstat), SMB_VFS_OP_LSTAT, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(shadow_copy2_fstat), SMB_VFS_OP_FSTAT, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(shadow_copy2_unlink), SMB_VFS_OP_UNLINK, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(shadow_copy2_chmod), SMB_VFS_OP_CHMOD, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(shadow_copy2_chown), SMB_VFS_OP_CHOWN, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(shadow_copy2_chdir), SMB_VFS_OP_CHDIR, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(shadow_copy2_ntimes), SMB_VFS_OP_NTIMES, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(shadow_copy2_symlink), SMB_VFS_OP_SYMLINK, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(shadow_copy2_readlink), SMB_VFS_OP_READLINK, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(shadow_copy2_link), SMB_VFS_OP_LINK, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(shadow_copy2_mknod), SMB_VFS_OP_MKNOD, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(shadow_copy2_realpath), SMB_VFS_OP_REALPATH, SMB_VFS_LAYER_TRANSPARENT}, + + /* NT File ACL operations */ + {SMB_VFS_OP(shadow_copy2_get_nt_acl), SMB_VFS_OP_GET_NT_ACL, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(shadow_copy2_set_nt_acl), SMB_VFS_OP_SET_NT_ACL, SMB_VFS_LAYER_TRANSPARENT}, + + /* POSIX ACL operations */ + {SMB_VFS_OP(shadow_copy2_chmod_acl), SMB_VFS_OP_CHMOD_ACL, SMB_VFS_LAYER_TRANSPARENT}, + + /* special shadown copy op */ + {SMB_VFS_OP(shadow_copy2_get_shadow_copy2_data), + SMB_VFS_OP_GET_SHADOW_COPY_DATA,SMB_VFS_LAYER_OPAQUE}, + + {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP} +}; + +NTSTATUS vfs_shadow_copy2_init(void); +NTSTATUS vfs_shadow_copy2_init(void) +{ + NTSTATUS ret; + + ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "shadow_copy2", shadow_copy2_ops); + + if (!NT_STATUS_IS_OK(ret)) + return ret; + + vfs_shadow_copy2_debug_level = debug_add_class("shadow_copy2"); + if (vfs_shadow_copy2_debug_level == -1) { + vfs_shadow_copy2_debug_level = DBGC_VFS; + DEBUG(0, ("%s: Couldn't register custom debugging class!\n", + "vfs_shadow_copy2_init")); + } else { + DEBUG(10, ("%s: Debug class number of '%s': %d\n", + "vfs_shadow_copy2_init","shadow_copy2",vfs_shadow_copy2_debug_level)); + } + + return ret; +} diff --git a/source3/modules/vfs_tsmsm.c b/source3/modules/vfs_tsmsm.c new file mode 100644 index 0000000000..fe791b24bf --- /dev/null +++ b/source3/modules/vfs_tsmsm.c @@ -0,0 +1,338 @@ +/* + Unix SMB/CIFS implementation. + Samba VFS module for handling offline files + with Tivoli Storage Manager Space Management + + (c) Alexander Bokovoy, 2007 + (c) Andrew Tridgell, 2007 + + 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, see <http://www.gnu.org/licenses/>. + */ +/* + This VFS module accepts following options: + tsmsm: hsm script = <path to hsm script> (/bin/true by default, i.e. does nothing) + hsm script should point to a shell script which accepts two arguments: + <operation> <filepath> + where <operation> is currently 'offline' to set offline status of the <filepath> + + tsmsm: online ratio = ratio to check reported size against actual file size (0.5 by default) + + The TSMSM VFS module tries to avoid calling expensive DMAPI calls with some heuristics + based on the fact that number of blocks reported of a file multiplied by 512 will be + bigger than 'online ratio' of actual size for online (non-migrated) files. + + If checks fail, we call DMAPI and ask for specific IBM attribute which present for + offline (migrated) files. If this attribute presents, we consider file offline. + */ + +#include "includes.h" + +#ifndef USE_DMAPI +#error "This module requires DMAPI support!" +#endif + +#ifdef HAVE_XFS_DMAPI_H +#include <xfs/dmapi.h> +#elif defined(HAVE_SYS_DMI_H) +#include <sys/dmi.h> +#elif defined(HAVE_SYS_JFSDMAPI_H) +#include <sys/jfsdmapi.h> +#elif defined(HAVE_SYS_DMAPI_H) +#include <sys/dmapi.h> +#elif defined(HAVE_DMAPI_H) +#include <dmapi.h> +#endif + +#ifndef _ISOC99_SOURCE +#define _ISOC99_SOURCE +#endif + +#include <math.h> + +/* optimisation tunables - used to avoid the DMAPI slow path */ +#define FILE_IS_ONLINE_RATIO 0.5 +#define DM_ATTRIB_OBJECT "IBMObj" +#define DM_ATTRIB_MIGRATED "IBMMig" + +struct tsmsm_struct { + dm_sessid_t sid; + float online_ratio; + char *hsmscript; +}; + +#define TSM_STRINGIFY(a) #a +#define TSM_TOSTRING(a) TSM_STRINGIFY(a) + +static void tsmsm_free_data(void **pptr) { + struct tsmsm_struct **tsmd = (struct tsmsm_struct **)pptr; + if(!tsmd) return; + TALLOC_FREE(*tsmd); +} + +static int tsmsm_connect(struct vfs_handle_struct *handle, + const char *service, + const char *user) { + struct tsmsm_struct *tsmd = TALLOC_ZERO_P(handle, struct tsmsm_struct); + const char *hsmscript, *tsmname; + const char *fres; + + if (!tsmd) { + DEBUG(0,("tsmsm_connect: out of memory!\n")); + return -1; + } + + tsmd->sid = *(dm_sessid_t*) dmapi_get_current_session(); + + if (tsmd->sid == DM_NO_SESSION) { + DEBUG(0,("tsmsm_connect: no DMAPI session for Samba is available!\n")); + TALLOC_FREE(tsmd); + return -1; + } + + tsmname = (handle->param ? handle->param : "tsmsm"); + hsmscript = lp_parm_const_string(SNUM(handle->conn), tsmname, + "hsm script", NULL); + if (hsmscript) { + tsmd->hsmscript = talloc_strdup(tsmd, hsmscript); + if(!tsmd->hsmscript) { + DEBUG(1, ("tsmsm_connect: can't allocate memory for hsm script path")); + TALLOC_FREE(tsmd); + return -1; + } + } else { + DEBUG(1, ("tsmsm_connect: can't call hsm script because it " + "is not set to anything in the smb.conf\n" + "Use %s: 'hsm script = path' to set it\n", + tsmname)); + TALLOC_FREE(tsmd); + return -1; + } + + fres = lp_parm_const_string(SNUM(handle->conn), tsmname, + "online ratio", TSM_TOSTRING(FILE_IS_ONLINE_RATIO)); + tsmd->online_ratio = strtof(fres, NULL); + if((tsmd->online_ratio == (float)0) || ((errno == ERANGE) && + ((tsmd->online_ratio == HUGE_VALF) || + (tsmd->online_ratio == HUGE_VALL)))) { + DEBUG(1, ("tsmsm_connect: error while getting online ratio from smb.conf." + "Default to %s.\n", TSM_TOSTRING(FILE_IS_ONLINE_RATIO))); + tsmd->online_ratio = FILE_IS_ONLINE_RATIO; + } + + /* Store the private data. */ + SMB_VFS_HANDLE_SET_DATA(handle, tsmd, tsmsm_free_data, + struct tsmsm_struct, return -1); + return SMB_VFS_NEXT_CONNECT(handle, service, user); +} + +static int tsmsm_is_offline(struct vfs_handle_struct *handle, + struct connection_struct *conn, + const char *path, + SMB_STRUCT_STAT *stbuf, + bool *offline) { + struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data; + void *dmhandle = NULL; + size_t dmhandle_len = 0; + size_t rlen; + dm_attrname_t dmname; + int ret; + + /* if the file has more than FILE_IS_ONLINE_RATIO of blocks available, + then assume it is not offline (it may not be 100%, as it could be sparse) */ + if (512 * (off_t)stbuf->st_blocks >= stbuf->st_size * tsmd->online_ratio) { + *offline = false; + DEBUG(10,("%s not offline: st_blocks=%ld st_size=%ld online_ratio=%.2f\n", + path, stbuf->st_blocks, stbuf->st_size, tsmd->online_ratio)); + return 0; + } + + /* using POSIX capabilities does not work here. It's a slow path, so + * become_root() is just as good anyway (tridge) + */ + + /* Also, AIX has DMAPI but no POSIX capablities support. In this case, + * we need to be root to do DMAPI manipulations. + */ + become_root(); + + /* go the slow DMAPI route */ + if (dm_path_to_handle((char*)path, &dmhandle, &dmhandle_len) != 0) { + ret = -1; + DEBUG(2,("dm_path_to_handle failed - assuming offline (%s) - %s\n", + path, strerror(errno))); + *offline = True; + goto done; + } + + memset(&dmname, 0, sizeof(dmname)); + strlcpy((char *)&dmname.an_chars[0], DM_ATTRIB_OBJECT, sizeof(dmname.an_chars)); + + ret = dm_get_dmattr(tsmd->sid, dmhandle, dmhandle_len, + DM_NO_TOKEN, &dmname, 0, NULL, &rlen); + + /* its offline if the IBMObj attribute exists */ + *offline = (ret == 0 || (ret == -1 && errno == E2BIG)); + + DEBUG(10,("dm_get_dmattr %s ret=%d (%s)\n", path, ret, strerror(errno))); + + ret = 0; + + dm_handle_free(dmhandle, dmhandle_len); + +done: + unbecome_root(); + return ret; +} + + +static bool tsmsm_aio_force(struct vfs_handle_struct *handle, struct files_struct *fsp) +{ + SMB_STRUCT_STAT sbuf; + struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data; + /* see if the file might be offline. This is called before each IO + to ensure we use AIO if the file is offline. We don't do the full dmapi + call as that would be too slow, instead we err on the side of using AIO + if the file might be offline + */ + if(SMB_VFS_FSTAT(fsp, &sbuf) == 0) { + DEBUG(10,("tsmsm_aio_force st_blocks=%ld st_size=%ld online_ratio=%.2f\n", + sbuf.st_blocks, sbuf.st_size, tsmd->online_ratio)); + return !(512 * (off_t)sbuf.st_blocks >= sbuf.st_size * tsmd->online_ratio); + } + return False; +} + +static ssize_t tsmsm_aio_return(struct vfs_handle_struct *handle, struct files_struct *fsp, + SMB_STRUCT_AIOCB *aiocb) +{ + ssize_t result; + + result = SMB_VFS_NEXT_AIO_RETURN(handle, fsp, aiocb); + if(result >= 0) { + notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED, + FILE_NOTIFY_CHANGE_ATTRIBUTES, + fsp->fsp_name); + } + + return result; +} + +static ssize_t tsmsm_sendfile(vfs_handle_struct *handle, int tofd, files_struct *fsp, const DATA_BLOB *hdr, + SMB_OFF_T offset, size_t n) +{ + bool file_online = tsmsm_aio_force(handle, fsp); + + if(!file_online) + return ENOSYS; + + return SMB_VFS_NEXT_SENDFILE(handle, tofd, fsp, hdr, offset, n); +} + +/* We do overload pread to allow notification when file becomes online after offline status */ +/* We don't intercept SMB_VFS_READ here because all file I/O now goes through SMB_VFS_PREAD instead */ +static ssize_t tsmsm_pread(struct vfs_handle_struct *handle, struct files_struct *fsp, + void *data, size_t n, SMB_OFF_T offset) { + ssize_t result; + bool notify_online = tsmsm_aio_force(handle, fsp); + + result = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset); + if((result != -1) && notify_online) { + /* We can't actually force AIO at this point (came here not from reply_read_and_X) + what we can do is to send notification that file became online + */ + notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED, + FILE_NOTIFY_CHANGE_ATTRIBUTES, + fsp->fsp_name); + } + + return result; +} + +static ssize_t tsmsm_pwrite(struct vfs_handle_struct *handle, struct files_struct *fsp, + void *data, size_t n, SMB_OFF_T offset) { + ssize_t result; + bool notify_online = tsmsm_aio_force(handle, fsp); + + result = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset); + if((result != -1) && notify_online) { + /* We can't actually force AIO at this point (came here not from reply_read_and_X) + what we can do is to send notification that file became online + */ + notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED, + FILE_NOTIFY_CHANGE_ATTRIBUTES, + fsp->fsp_name); + } + + return result; +} + +static int tsmsm_set_offline(struct vfs_handle_struct *handle, struct connection_struct *conn, + const char *path) { + struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data; + int result = 0; + char *command; + + /* Now, call the script */ + command = talloc_asprintf(tsmd, "%s offline \"%s\"", tsmd->hsmscript, path); + if(!command) { + DEBUG(1, ("tsmsm_set_offline: can't allocate memory to run hsm script")); + return -1; + } + DEBUG(10, ("tsmsm_set_offline: Running [%s]\n", command)); + if((result = smbrun(command, NULL)) != 0) { + DEBUG(1,("tsmsm_set_offline: Running [%s] returned %d\n", command, result)); + } + TALLOC_FREE(command); + return result; +} + +static bool tsmsm_is_remotestorage(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path) { + return True; +} + +static vfs_op_tuple vfs_tsmsm_ops[] = { + + /* Disk operations */ + + {SMB_VFS_OP(tsmsm_connect), SMB_VFS_OP_CONNECT, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(tsmsm_aio_force), SMB_VFS_OP_AIO_FORCE, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(tsmsm_aio_return), SMB_VFS_OP_AIO_RETURN, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(tsmsm_pread), SMB_VFS_OP_PREAD, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(tsmsm_pwrite), SMB_VFS_OP_PWRITE, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(tsmsm_sendfile), SMB_VFS_OP_SENDFILE, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(tsmsm_is_offline),SMB_VFS_OP_IS_OFFLINE, + SMB_VFS_LAYER_OPAQUE}, + {SMB_VFS_OP(tsmsm_set_offline),SMB_VFS_OP_SET_OFFLINE, + SMB_VFS_LAYER_OPAQUE}, + {SMB_VFS_OP(tsmsm_is_remotestorage),SMB_VFS_OP_IS_REMOTESTORAGE, + SMB_VFS_LAYER_OPAQUE}, + + /* Finish VFS operations definition */ + + {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, + SMB_VFS_LAYER_NOOP} +}; + +NTSTATUS vfs_tsmsm_init(void); +NTSTATUS vfs_tsmsm_init(void) +{ + return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, + "tsmsm", vfs_tsmsm_ops); +} |