mirror of
https://github.com/samba-team/samba.git
synced 2024-12-24 21:34:56 +03:00
7d5f0e0893
(This used to be commit 2f29f80e07
)
484 lines
12 KiB
C
484 lines
12 KiB
C
/*
|
|
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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
#include "vfs_posix.h"
|
|
#include "lib/util/unix_privs.h"
|
|
#include "librpc/gen_ndr/ndr_xattr.h"
|
|
#include "param/param.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(5,("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_xattr_tdb(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_xattr_tdb(pvfs, fname);
|
|
}
|
|
return unlink_xattr_system(pvfs, fname);
|
|
}
|
|
|
|
|
|
/*
|
|
load a NDR structure from a xattr
|
|
*/
|
|
_PUBLIC_ 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, lp_iconv_convenience(pvfs->ntvfs->ctx->lp_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
|
|
*/
|
|
_PUBLIC_ 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, lp_iconv_convenience(global_loadparm), 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_DosInfo2 *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,
|
|
(ndr_pull_flags_fn_t)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:
|
|
info2 = &attrib.info.info2;
|
|
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;
|
|
if (name->dos.flags & XATTR_ATTRIB_FLAG_STICKY_WRITE_TIME) {
|
|
name->dos.write_time = info2->write_time;
|
|
}
|
|
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_DosInfo2 *info2;
|
|
|
|
if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
attrib.version = 2;
|
|
info2 = &attrib.info.info2;
|
|
|
|
name->dos.attrib = pvfs_attrib_normalise(name->dos.attrib, name->st.st_mode);
|
|
|
|
info2->attrib = name->dos.attrib;
|
|
info2->ea_size = name->dos.ea_size;
|
|
info2->size = name->st.st_size;
|
|
info2->alloc_size = name->dos.alloc_size;
|
|
info2->create_time = name->dos.create_time;
|
|
info2->change_time = name->dos.change_time;
|
|
info2->write_time = name->dos.write_time;
|
|
info2->flags = name->dos.flags;
|
|
info2->name = "";
|
|
|
|
return pvfs_xattr_ndr_save(pvfs, name->full_name, fd,
|
|
XATTR_DOSATTRIB_NAME, &attrib,
|
|
(ndr_push_flags_fn_t)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 (!(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, (ndr_pull_flags_fn_t)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,
|
|
(ndr_push_flags_fn_t)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,
|
|
(ndr_pull_flags_fn_t)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,
|
|
(ndr_push_flags_fn_t)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,
|
|
(ndr_pull_flags_fn_t)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,
|
|
(ndr_push_flags_fn_t)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);
|
|
}
|