/* Unix SMB/CIFS implementation. POSIX NTVFS backend - xattr support Copyright (C) Andrew Tridgell 2004 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 . */ #include "includes.h" #include "vfs_posix.h" #include "../lib/util/unix_privs.h" #include "librpc/gen_ndr/ndr_xattr.h" #include "param/param.h" #include "ntvfs/posix/posix_eadb_proto.h" /* pull a xattr as a blob */ static NTSTATUS pull_xattr_blob(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, const char *attr_name, const char *fname, int fd, size_t estimated_size, DATA_BLOB *blob) { NTSTATUS status; if (pvfs->ea_db) { return pull_xattr_blob_tdb(pvfs, mem_ctx, attr_name, fname, fd, estimated_size, blob); } status = pull_xattr_blob_system(pvfs, mem_ctx, attr_name, fname, fd, estimated_size, blob); /* if the filesystem doesn't support them, then tell pvfs not to try again */ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)|| NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)|| NT_STATUS_EQUAL(status, NT_STATUS_INVALID_SYSTEM_SERVICE)) { DEBUG(2,("pvfs_xattr: xattr not supported in filesystem: %s\n", nt_errstr(status))); pvfs->flags &= ~PVFS_FLAG_XATTR_ENABLE; status = NT_STATUS_NOT_FOUND; } return status; } /* push a xattr as a blob */ static NTSTATUS push_xattr_blob(struct pvfs_state *pvfs, const char *attr_name, const char *fname, int fd, const DATA_BLOB *blob) { if (pvfs->ea_db) { return push_xattr_blob_tdb(pvfs, attr_name, fname, fd, blob); } return push_xattr_blob_system(pvfs, attr_name, fname, fd, blob); } /* delete a xattr */ static NTSTATUS delete_xattr(struct pvfs_state *pvfs, const char *attr_name, const char *fname, int fd) { if (pvfs->ea_db) { return delete_posix_eadb(pvfs, attr_name, fname, fd); } return delete_xattr_system(pvfs, attr_name, fname, fd); } /* a hook called on unlink - allows the tdb xattr backend to cleanup */ NTSTATUS pvfs_xattr_unlink_hook(struct pvfs_state *pvfs, const char *fname) { if (pvfs->ea_db) { return unlink_posix_eadb(pvfs, fname); } return unlink_xattr_system(pvfs, fname); } /* load a NDR structure from a xattr */ NTSTATUS pvfs_xattr_ndr_load(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, const char *fname, int fd, const char *attr_name, void *p, void *pull_fn) { NTSTATUS status; DATA_BLOB blob; enum ndr_err_code ndr_err; status = pull_xattr_blob(pvfs, mem_ctx, attr_name, fname, fd, XATTR_DOSATTRIB_ESTIMATED_SIZE, &blob); if (!NT_STATUS_IS_OK(status)) { return status; } /* pull the blob */ ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, p, (ndr_pull_flags_fn_t)pull_fn); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { return ndr_map_error2ntstatus(ndr_err); } data_blob_free(&blob); return NT_STATUS_OK; } /* save a NDR structure into a xattr */ NTSTATUS pvfs_xattr_ndr_save(struct pvfs_state *pvfs, const char *fname, int fd, const char *attr_name, void *p, void *push_fn) { TALLOC_CTX *mem_ctx = talloc_new(NULL); DATA_BLOB blob; NTSTATUS status; enum ndr_err_code ndr_err; ndr_err = ndr_push_struct_blob(&blob, mem_ctx, p, (ndr_push_flags_fn_t)push_fn); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { talloc_free(mem_ctx); return ndr_map_error2ntstatus(ndr_err); } status = push_xattr_blob(pvfs, attr_name, fname, fd, &blob); talloc_free(mem_ctx); return status; } /* fill in file attributes from extended attributes */ NTSTATUS pvfs_dosattrib_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd) { NTSTATUS status; struct xattr_DosAttrib attrib; TALLOC_CTX *mem_ctx = talloc_new(name); struct xattr_DosInfo1 *info1; struct xattr_DosInfo2Old *info2; if (name->stream_name != NULL) { name->stream_exists = false; } else { name->stream_exists = true; } if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) { return NT_STATUS_OK; } status = pvfs_xattr_ndr_load(pvfs, mem_ctx, name->full_name, fd, XATTR_DOSATTRIB_NAME, &attrib, (void *) ndr_pull_xattr_DosAttrib); /* not having a DosAttrib is not an error */ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { talloc_free(mem_ctx); return pvfs_stream_info(pvfs, name, fd); } if (!NT_STATUS_IS_OK(status)) { talloc_free(mem_ctx); return status; } switch (attrib.version) { case 1: info1 = &attrib.info.info1; name->dos.attrib = pvfs_attrib_normalise(info1->attrib, name->st.st_mode); name->dos.ea_size = info1->ea_size; if (name->st.st_size == info1->size) { name->dos.alloc_size = pvfs_round_alloc_size(pvfs, info1->alloc_size); } if (!null_nttime(info1->create_time)) { name->dos.create_time = info1->create_time; } if (!null_nttime(info1->change_time)) { name->dos.change_time = info1->change_time; } name->dos.flags = 0; break; case 2: /* * Note: This is only used to parse existing values from disk * We use xattr_DosInfo1 again for storing new values */ info2 = &attrib.info.oldinfo2; name->dos.attrib = pvfs_attrib_normalise(info2->attrib, name->st.st_mode); name->dos.ea_size = info2->ea_size; if (name->st.st_size == info2->size) { name->dos.alloc_size = pvfs_round_alloc_size(pvfs, info2->alloc_size); } if (!null_nttime(info2->create_time)) { name->dos.create_time = info2->create_time; } if (!null_nttime(info2->change_time)) { name->dos.change_time = info2->change_time; } name->dos.flags = info2->flags; break; default: DEBUG(0,("ERROR: Unsupported xattr DosAttrib version %d on '%s'\n", attrib.version, name->full_name)); talloc_free(mem_ctx); return NT_STATUS_INVALID_LEVEL; } talloc_free(mem_ctx); status = pvfs_stream_info(pvfs, name, fd); return status; } /* save the file attribute into the xattr */ NTSTATUS pvfs_dosattrib_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd) { struct xattr_DosAttrib attrib; struct xattr_DosInfo1 *info1; if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) { return NT_STATUS_OK; } attrib.version = 1; info1 = &attrib.info.info1; name->dos.attrib = pvfs_attrib_normalise(name->dos.attrib, name->st.st_mode); info1->attrib = name->dos.attrib; info1->ea_size = name->dos.ea_size; info1->size = name->st.st_size; info1->alloc_size = name->dos.alloc_size; info1->create_time = name->dos.create_time; info1->change_time = name->dos.change_time; return pvfs_xattr_ndr_save(pvfs, name->full_name, fd, XATTR_DOSATTRIB_NAME, &attrib, (void *) ndr_push_xattr_DosAttrib); } /* load the set of DOS EAs */ NTSTATUS pvfs_doseas_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd, struct xattr_DosEAs *eas) { NTSTATUS status; ZERO_STRUCTP(eas); if (name->stream_name) { /* We don't support EAs on streams */ return NT_STATUS_INVALID_PARAMETER; } if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) { return NT_STATUS_OK; } status = pvfs_xattr_ndr_load(pvfs, eas, name->full_name, fd, XATTR_DOSEAS_NAME, eas, (void *) ndr_pull_xattr_DosEAs); if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { return NT_STATUS_OK; } return status; } /* save the set of DOS EAs */ NTSTATUS pvfs_doseas_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd, struct xattr_DosEAs *eas) { if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) { return NT_STATUS_OK; } return pvfs_xattr_ndr_save(pvfs, name->full_name, fd, XATTR_DOSEAS_NAME, eas, (void *) ndr_push_xattr_DosEAs); } /* load the set of streams from extended attributes */ NTSTATUS pvfs_streams_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd, struct xattr_DosStreams *streams) { NTSTATUS status; ZERO_STRUCTP(streams); if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) { return NT_STATUS_OK; } status = pvfs_xattr_ndr_load(pvfs, streams, name->full_name, fd, XATTR_DOSSTREAMS_NAME, streams, (void *) ndr_pull_xattr_DosStreams); if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { return NT_STATUS_OK; } return status; } /* save the set of streams into filesystem xattr */ NTSTATUS pvfs_streams_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd, struct xattr_DosStreams *streams) { if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) { return NT_STATUS_OK; } return pvfs_xattr_ndr_save(pvfs, name->full_name, fd, XATTR_DOSSTREAMS_NAME, streams, (void *) ndr_push_xattr_DosStreams); } /* load the current ACL from extended attributes */ NTSTATUS pvfs_acl_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd, struct xattr_NTACL *acl) { NTSTATUS status; ZERO_STRUCTP(acl); if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) { return NT_STATUS_NOT_FOUND; } status = pvfs_xattr_ndr_load(pvfs, acl, name->full_name, fd, XATTR_NTACL_NAME, acl, (void *) ndr_pull_xattr_NTACL); return status; } /* save the acl for a file into filesystem xattr */ NTSTATUS pvfs_acl_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd, struct xattr_NTACL *acl) { NTSTATUS status; void *privs; if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) { return NT_STATUS_OK; } /* this xattr is in the "system" namespace, so we need admin privileges to set it */ privs = root_privileges(); status = pvfs_xattr_ndr_save(pvfs, name->full_name, fd, XATTR_NTACL_NAME, acl, (void *) ndr_push_xattr_NTACL); talloc_free(privs); return status; } /* create a zero length xattr with the given name */ NTSTATUS pvfs_xattr_create(struct pvfs_state *pvfs, const char *fname, int fd, const char *attr_prefix, const char *attr_name) { NTSTATUS status; DATA_BLOB blob = data_blob(NULL, 0); char *aname = talloc_asprintf(NULL, "%s%s", attr_prefix, attr_name); if (aname == NULL) { return NT_STATUS_NO_MEMORY; } status = push_xattr_blob(pvfs, aname, fname, fd, &blob); talloc_free(aname); return status; } /* delete a xattr with the given name */ NTSTATUS pvfs_xattr_delete(struct pvfs_state *pvfs, const char *fname, int fd, const char *attr_prefix, const char *attr_name) { NTSTATUS status; char *aname = talloc_asprintf(NULL, "%s%s", attr_prefix, attr_name); if (aname == NULL) { return NT_STATUS_NO_MEMORY; } status = delete_xattr(pvfs, aname, fname, fd); talloc_free(aname); return status; } /* load a xattr with the given name */ NTSTATUS pvfs_xattr_load(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, const char *fname, int fd, const char *attr_prefix, const char *attr_name, size_t estimated_size, DATA_BLOB *blob) { NTSTATUS status; char *aname = talloc_asprintf(mem_ctx, "%s%s", attr_prefix, attr_name); if (aname == NULL) { return NT_STATUS_NO_MEMORY; } status = pull_xattr_blob(pvfs, mem_ctx, aname, fname, fd, estimated_size, blob); talloc_free(aname); return status; } /* save a xattr with the given name */ NTSTATUS pvfs_xattr_save(struct pvfs_state *pvfs, const char *fname, int fd, const char *attr_prefix, const char *attr_name, const DATA_BLOB *blob) { NTSTATUS status; char *aname = talloc_asprintf(NULL, "%s%s", attr_prefix, attr_name); if (aname == NULL) { return NT_STATUS_NO_MEMORY; } status = push_xattr_blob(pvfs, aname, fname, fd, blob); talloc_free(aname); return status; } /* probe for system support for xattrs */ void pvfs_xattr_probe(struct pvfs_state *pvfs) { TALLOC_CTX *tmp_ctx = talloc_new(pvfs); DATA_BLOB blob; pull_xattr_blob(pvfs, tmp_ctx, "user.XattrProbe", pvfs->base_directory, -1, 1, &blob); pull_xattr_blob(pvfs, tmp_ctx, "security.XattrProbe", pvfs->base_directory, -1, 1, &blob); talloc_free(tmp_ctx); }