diff options
author | Andrew Tridgell <tridge@samba.org> | 2004-11-17 05:58:04 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 13:05:54 -0500 |
commit | ae7caf08c1f47f3ad08856cfea2a3e6e956b48ab (patch) | |
tree | a70a8867b4d85c5d34a1f30e6aff22cf37052d6f /source4 | |
parent | ca90965a3a2dabfc50be6d155c20004a61f81318 (diff) | |
download | samba-ae7caf08c1f47f3ad08856cfea2a3e6e956b48ab.tar.gz samba-ae7caf08c1f47f3ad08856cfea2a3e6e956b48ab.tar.bz2 samba-ae7caf08c1f47f3ad08856cfea2a3e6e956b48ab.zip |
r3798: added support for alternate data streams in xattrs into pvfs.
The trickiest part about this was getting the sharing and locking
rules right, as alternate streams are separate locking spaces from the
main file for the purposes of byte range locking, and separate for
most share violation rules.
I suspect there are still problems with delete on close with alternate
data streams. I'll look at that next.
(This used to be commit b6452c4a2068cf7e837778559da002ae191b508a)
Diffstat (limited to 'source4')
-rw-r--r-- | source4/include/smb_interfaces.h | 2 | ||||
-rw-r--r-- | source4/include/structs.h | 1 | ||||
-rw-r--r-- | source4/librpc/idl/xattr.idl | 8 | ||||
-rw-r--r-- | source4/ntvfs/common/opendb.c | 15 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_fileinfo.c | 1 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_lock.c | 18 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_open.c | 126 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_qfileinfo.c | 16 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_read.c | 13 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_rename.c | 10 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_resolve.c | 45 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_setfileinfo.c | 28 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_shortname.c | 23 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_streams.c | 322 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_unlink.c | 33 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_util.c | 27 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_write.c | 16 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_xattr.c | 115 | ||||
-rw-r--r-- | source4/ntvfs/posix/vfs_posix.h | 13 | ||||
-rw-r--r-- | source4/torture/raw/streams.c | 14 |
20 files changed, 736 insertions, 110 deletions
diff --git a/source4/include/smb_interfaces.h b/source4/include/smb_interfaces.h index acc2d503bb..43cb8af5a7 100644 --- a/source4/include/smb_interfaces.h +++ b/source4/include/smb_interfaces.h @@ -532,7 +532,7 @@ union smb_fileinfo { enum smb_fileinfo_level level; union smb_fileinfo_in in; - struct { + struct stream_information { uint_t num_streams; struct stream_struct *streams; } out; diff --git a/source4/include/structs.h b/source4/include/structs.h index 4ec8f1f4d3..9e64361dbf 100644 --- a/source4/include/structs.h +++ b/source4/include/structs.h @@ -91,6 +91,7 @@ struct pvfs_dir; struct pvfs_filename; struct pvfs_state; struct pvfs_file; +struct pvfs_file_handle; struct dcesrv_context; struct dcesrv_interface; diff --git a/source4/librpc/idl/xattr.idl b/source4/librpc/idl/xattr.idl index be587ffb88..b2e13bfbe7 100644 --- a/source4/librpc/idl/xattr.idl +++ b/source4/librpc/idl/xattr.idl @@ -56,10 +56,14 @@ interface xattr const int XATTR_STREAM_FLAG_INTERNAL = 0x00000001; + /* stream data is stored in attributes with the given prefix */ + const string XATTR_DOSSTREAM_PREFIX = "user.DosStream."; + typedef struct { - utf8string name; - uint64 size; uint32 flags; + uint64 size; + uint64 alloc_size; + utf8string name; } xattr_DosStream; typedef [public] struct { diff --git a/source4/ntvfs/common/opendb.c b/source4/ntvfs/common/opendb.c index a07b657c33..64bed53c8b 100644 --- a/source4/ntvfs/common/opendb.c +++ b/source4/ntvfs/common/opendb.c @@ -54,6 +54,7 @@ struct odb_context { struct odb_entry { servid_t server; void *file_handle; + uint32_t stream_id; uint32_t share_access; uint32_t create_options; uint32_t access_mask; @@ -167,14 +168,20 @@ static BOOL share_conflict(struct odb_entry *e1, struct odb_entry *e2) return False; } - /* check the basic share access */ + /* data IO access masks. This is skipped if the two open handles + are on different streams (as in that case the masks don't + interact) */ + if (e1->stream_id != e2->stream_id) { + return False; + } + CHECK_MASK(e1->access_mask, e2->share_access, SA_RIGHT_FILE_WRITE_APPEND, NTCREATEX_SHARE_ACCESS_WRITE); CHECK_MASK(e2->access_mask, e1->share_access, SA_RIGHT_FILE_WRITE_APPEND, NTCREATEX_SHARE_ACCESS_WRITE); - + CHECK_MASK(e1->access_mask, e2->share_access, SA_RIGHT_FILE_READ_EXEC, NTCREATEX_SHARE_ACCESS_READ); @@ -203,6 +210,7 @@ static BOOL share_conflict(struct odb_entry *e1, struct odb_entry *e2) rules */ NTSTATUS odb_open_file(struct odb_lock *lck, void *file_handle, + uint32_t stream_id, uint32_t share_access, uint32_t create_options, uint32_t access_mask) { @@ -217,6 +225,7 @@ NTSTATUS odb_open_file(struct odb_lock *lck, void *file_handle, e.server = odb->server; e.file_handle = file_handle; + e.stream_id = stream_id; e.share_access = share_access; e.create_options = create_options; e.access_mask = access_mask; @@ -273,6 +282,7 @@ NTSTATUS odb_open_file_pending(struct odb_lock *lck, void *private) e.server = odb->server; e.file_handle = NULL; + e.stream_id = 0; e.share_access = 0; e.create_options = 0; e.access_mask = 0; @@ -496,6 +506,7 @@ NTSTATUS odb_can_open(struct odb_context *odb, DATA_BLOB *key, e.server = odb->server; e.file_handle = NULL; + e.stream_id = 0; e.share_access = share_access; e.create_options = create_options; e.access_mask = access_mask; diff --git a/source4/ntvfs/posix/pvfs_fileinfo.c b/source4/ntvfs/posix/pvfs_fileinfo.c index 2419893049..dbf18edcce 100644 --- a/source4/ntvfs/posix/pvfs_fileinfo.c +++ b/source4/ntvfs/posix/pvfs_fileinfo.c @@ -136,3 +136,4 @@ mode_t pvfs_fileperms(struct pvfs_state *pvfs, uint32 attrib) return mode; } + diff --git a/source4/ntvfs/posix/pvfs_lock.c b/source4/ntvfs/posix/pvfs_lock.c index c0d0133f03..74ee48ba15 100644 --- a/source4/ntvfs/posix/pvfs_lock.c +++ b/source4/ntvfs/posix/pvfs_lock.c @@ -41,7 +41,7 @@ NTSTATUS pvfs_check_lock(struct pvfs_state *pvfs, } return brl_locktest(pvfs->brl_context, - &f->handle->locking_key, + &f->handle->brl_locking_key, f->fnum, smbpid, offset, count, rw); @@ -73,7 +73,7 @@ static void pvfs_lock_async_failed(struct pvfs_state *pvfs, /* undo the locks we just did */ for (i=i-1;i>=0;i--) { brl_unlock(pvfs->brl_context, - &f->handle->locking_key, + &f->handle->brl_locking_key, locks[i].pid, f->fnum, locks[i].offset, @@ -118,7 +118,7 @@ static void pvfs_pending_lock_continue(void *private, enum pvfs_wait_notice reas DLIST_REMOVE(f->pending_list, pending); status = brl_lock(pvfs->brl_context, - &f->handle->locking_key, + &f->handle->brl_locking_key, req->smbpid, f->fnum, locks[pending->pending_lock].offset, @@ -134,7 +134,7 @@ static void pvfs_pending_lock_continue(void *private, enum pvfs_wait_notice reas if (NT_STATUS_IS_OK(status) || timed_out) { NTSTATUS status2; status2 = brl_remove_pending(pvfs->brl_context, - &f->handle->locking_key, pending); + &f->handle->brl_locking_key, pending); if (!NT_STATUS_IS_OK(status2)) { DEBUG(0,("pvfs_lock: failed to remove pending lock - %s\n", nt_errstr(status2))); } @@ -171,7 +171,7 @@ static void pvfs_pending_lock_continue(void *private, enum pvfs_wait_notice reas } status = brl_lock(pvfs->brl_context, - &f->handle->locking_key, + &f->handle->brl_locking_key, req->smbpid, f->fnum, locks[i].offset, @@ -216,7 +216,7 @@ void pvfs_lock_close(struct pvfs_state *pvfs, struct pvfs_file *f) if (f->lock_count || f->pending_list) { DEBUG(5,("pvfs_lock: removing %.0f locks on close\n", (double)f->lock_count)); - brl_close(f->pvfs->brl_context, &f->handle->locking_key, f->fnum); + brl_close(f->pvfs->brl_context, &f->handle->brl_locking_key, f->fnum); f->lock_count = 0; } @@ -338,7 +338,7 @@ NTSTATUS pvfs_lock(struct ntvfs_module_context *ntvfs, for (i=0;i<lck->lockx.in.ulock_cnt;i++) { status = brl_unlock(pvfs->brl_context, - &f->handle->locking_key, + &f->handle->brl_locking_key, locks[i].pid, f->fnum, locks[i].offset, @@ -357,7 +357,7 @@ NTSTATUS pvfs_lock(struct ntvfs_module_context *ntvfs, } status = brl_lock(pvfs->brl_context, - &f->handle->locking_key, + &f->handle->brl_locking_key, locks[i].pid, f->fnum, locks[i].offset, @@ -381,7 +381,7 @@ NTSTATUS pvfs_lock(struct ntvfs_module_context *ntvfs, /* undo the locks we just did */ for (i=i-1;i>=0;i--) { brl_unlock(pvfs->brl_context, - &f->handle->locking_key, + &f->handle->brl_locking_key, locks[i].pid, f->fnum, locks[i].offset, diff --git a/source4/ntvfs/posix/pvfs_open.c b/source4/ntvfs/posix/pvfs_open.c index 4f089a8c07..a772feb623 100644 --- a/source4/ntvfs/posix/pvfs_open.c +++ b/source4/ntvfs/posix/pvfs_open.c @@ -104,7 +104,7 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs, uint32_t create_action; if (name->stream_name) { - return NT_STATUS_OBJECT_NAME_INVALID; + return NT_STATUS_NOT_A_DIRECTORY; } /* if the client says it must be a directory, and it isn't, @@ -163,7 +163,8 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs, f->handle->pvfs = pvfs; f->handle->name = talloc_steal(f->handle, name); f->handle->fd = -1; - f->handle->locking_key = data_blob(NULL, 0); + f->handle->odb_locking_key = data_blob(NULL, 0); + f->handle->brl_locking_key = data_blob(NULL, 0); f->handle->create_options = io->generic.in.create_options; f->handle->seek_offset = 0; f->handle->position = 0; @@ -243,7 +244,7 @@ static int pvfs_handle_destructor(void *p) struct odb_lock *lck; NTSTATUS status; - lck = odb_lock(h, h->pvfs->odb_context, &h->locking_key); + lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key); if (lck == NULL) { DEBUG(0,("Unable to lock opendb for close\n")); return 0; @@ -278,9 +279,8 @@ static int pvfs_fnum_destructor(void *p) /* - form the lock context used for byte range locking and opendb - locking. Note that we must zero here to take account of - possible padding on some architectures + form the lock context used for opendb locking. Note that we must + zero here to take account of possible padding on some architectures */ static NTSTATUS pvfs_locking_key(struct pvfs_filename *name, TALLOC_CTX *mem_ctx, DATA_BLOB *key) @@ -302,6 +302,37 @@ static NTSTATUS pvfs_locking_key(struct pvfs_filename *name, return NT_STATUS_OK; } +/* + form the lock context used for byte range locking. This is separate + from the locking key used for opendb locking as it needs to take + account of file streams (each stream is a separate byte range + locking space) +*/ +static NTSTATUS pvfs_brl_locking_key(struct pvfs_filename *name, + TALLOC_CTX *mem_ctx, DATA_BLOB *key) +{ + DATA_BLOB odb_key; + NTSTATUS status; + status = pvfs_locking_key(name, mem_ctx, &odb_key); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (name->stream_name == NULL) { + *key = odb_key; + return NT_STATUS_OK; + } + *key = data_blob_talloc(mem_ctx, NULL, + odb_key.length + strlen(name->stream_name) + 1); + if (key->data == NULL) { + return NT_STATUS_NO_MEMORY; + } + memcpy(key->data, odb_key.data, odb_key.length); + memcpy(key->data + odb_key.length, + name->stream_name, strlen(name->stream_name)+1); + data_blob_free(&odb_key); + return NT_STATUS_OK; +} + /* create a new file @@ -361,6 +392,16 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, return pvfs_map_errno(pvfs, errno); } + /* if this was a stream create then create the stream as well */ + if (name->stream_name) { + status = pvfs_stream_create(pvfs, name, fd); + if (!NT_STATUS_IS_OK(status)) { + idr_remove(pvfs->idtree_fnum, fnum); + close(fd); + return status; + } + } + /* re-resolve the open fd */ status = pvfs_resolve_name_fd(pvfs, fd, name); if (!NT_STATUS_IS_OK(status)) { @@ -379,7 +420,14 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, /* form the lock context used for byte range locking and opendb locking */ - status = pvfs_locking_key(name, f->handle, &f->handle->locking_key); + status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key); + if (!NT_STATUS_IS_OK(status)) { + idr_remove(pvfs->idtree_fnum, fnum); + close(fd); + return status; + } + + status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key); if (!NT_STATUS_IS_OK(status)) { idr_remove(pvfs->idtree_fnum, fnum); close(fd); @@ -387,7 +435,7 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, } /* grab a lock on the open file record */ - lck = odb_lock(req, pvfs->odb_context, &f->handle->locking_key); + lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key); if (lck == NULL) { DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n", name->full_name)); @@ -398,7 +446,7 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, return NT_STATUS_INTERNAL_DB_CORRUPTION; } - status = odb_open_file(lck, f->handle, + status = odb_open_file(lck, f->handle, name->stream_id, share_access, create_options, access_mask); talloc_free(lck); if (!NT_STATUS_IS_OK(status)) { @@ -463,7 +511,7 @@ struct pvfs_open_retry { struct smbsrv_request *req; union smb_open *io; void *wait_handle; - DATA_BLOB locking_key; + DATA_BLOB odb_locking_key; }; /* destroy a pending open request */ @@ -471,9 +519,9 @@ static int pvfs_retry_destructor(void *ptr) { struct pvfs_open_retry *r = ptr; struct pvfs_state *pvfs = r->ntvfs->private_data; - if (r->locking_key.data) { + if (r->odb_locking_key.data) { struct odb_lock *lck; - lck = odb_lock(r->req, pvfs->odb_context, &r->locking_key); + lck = odb_lock(r->req, pvfs->odb_context, &r->odb_locking_key); if (lck != NULL) { odb_remove_pending(lck, r); } @@ -512,7 +560,7 @@ static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason) /* the pending odb entry is already removed. We use a null locking key to indicate this */ - data_blob_free(&r->locking_key); + data_blob_free(&r->odb_locking_key); talloc_free(r); /* try the open again, which could trigger another retry setup @@ -649,9 +697,9 @@ static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs, r->ntvfs = ntvfs; r->req = req; r->io = io; - r->locking_key = data_blob_talloc(r, - f->handle->locking_key.data, - f->handle->locking_key.length); + r->odb_locking_key = data_blob_talloc(r, + f->handle->odb_locking_key.data, + f->handle->odb_locking_key.length); end_time = timeval_add(&req->request_time, 0, pvfs->sharing_violation_delay); @@ -693,14 +741,14 @@ static NTSTATUS pvfs_open_t2open(struct ntvfs_module_context *ntvfs, } if (io->t2open.in.open_func & OPENX_OPEN_FUNC_CREATE) { - if (!name->exists) return NT_STATUS_ACCESS_DENIED; + if (!name->stream_exists) return NT_STATUS_ACCESS_DENIED; } if (io->t2open.in.open_func & OPENX_OPEN_FUNC_TRUNC) { - if (name->exists) return NT_STATUS_ACCESS_DENIED; + if (name->stream_exists) return NT_STATUS_ACCESS_DENIED; return NT_STATUS_OBJECT_NAME_NOT_FOUND; } if ((io->t2open.in.open_func & 0xF) == OPENX_OPEN_FUNC_FAIL) { - if (!name->exists) return NT_STATUS_ACCESS_DENIED; + if (!name->stream_exists) return NT_STATUS_ACCESS_DENIED; return NT_STATUS_OBJECT_NAME_COLLISION; } @@ -725,6 +773,7 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, uint32_t create_options; uint32_t share_access; uint32_t access_mask; + BOOL stream_existed; if (io->generic.level == RAW_OPEN_T2OPEN) { return pvfs_open_t2open(ntvfs, req, io); @@ -777,21 +826,21 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, break; case NTCREATEX_DISP_OPEN: - if (!name->exists) { + if (!name->stream_exists) { return NT_STATUS_OBJECT_NAME_NOT_FOUND; } flags = 0; break; case NTCREATEX_DISP_OVERWRITE: - if (!name->exists) { + if (!name->stream_exists) { return NT_STATUS_OBJECT_NAME_NOT_FOUND; } flags = O_TRUNC; break; case NTCREATEX_DISP_CREATE: - if (name->exists) { + if (name->stream_exists) { return NT_STATUS_OBJECT_NAME_COLLISION; } flags = 0; @@ -876,14 +925,20 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, /* form the lock context used for byte range locking and opendb locking */ - status = pvfs_locking_key(name, f->handle, &f->handle->locking_key); + status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key); + if (!NT_STATUS_IS_OK(status)) { + idr_remove(pvfs->idtree_fnum, f->fnum); + return status; + } + + status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key); if (!NT_STATUS_IS_OK(status)) { idr_remove(pvfs->idtree_fnum, f->fnum); return status; } /* get a lock on this file before the actual open */ - lck = odb_lock(req, pvfs->odb_context, &f->handle->locking_key); + lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key); if (lck == NULL) { DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n", name->full_name)); @@ -902,7 +957,7 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, /* see if we are allowed to open at the same time as existing opens */ - status = odb_open_file(lck, f->handle, + status = odb_open_file(lck, f->handle, f->handle->name->stream_id, share_access, create_options, access_mask); /* on a sharing violation we need to retry when the file is closed by @@ -928,6 +983,17 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, f->handle->fd = fd; + stream_existed = name->stream_exists; + + /* if this was a stream create then create the stream as well */ + if (!name->stream_exists) { + status = pvfs_stream_create(pvfs, f->handle->name, fd); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(lck); + return status; + } + } + /* re-resolve the open fd */ status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name); if (!NT_STATUS_IS_OK(status)) { @@ -935,8 +1001,9 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, return status; } - if (io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE || - io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE_IF) { + if (f->handle->name->stream_id == 0 && + (io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE || + io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE_IF)) { /* for overwrite we need to replace file permissions */ uint32_t attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE; mode_t mode = pvfs_fileperms(pvfs, attrib); @@ -956,7 +1023,8 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, io->generic.out.oplock_level = NO_OPLOCK; io->generic.out.fnum = f->fnum; - io->generic.out.create_action = NTCREATEX_ACTION_EXISTED; + io->generic.out.create_action = stream_existed? + NTCREATEX_ACTION_EXISTED:NTCREATEX_ACTION_CREATED; io->generic.out.create_time = name->dos.create_time; io->generic.out.access_time = name->dos.access_time; io->generic.out.write_time = name->dos.write_time; @@ -1069,7 +1137,7 @@ NTSTATUS pvfs_change_create_options(struct pvfs_state *pvfs, return NT_STATUS_CANNOT_DELETE; } - lck = odb_lock(req, pvfs->odb_context, &f->handle->locking_key); + lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key); if (lck == NULL) { return NT_STATUS_INTERNAL_DB_CORRUPTION; } diff --git a/source4/ntvfs/posix/pvfs_qfileinfo.c b/source4/ntvfs/posix/pvfs_qfileinfo.c index 3eaa7865e8..3959fbfc16 100644 --- a/source4/ntvfs/posix/pvfs_qfileinfo.c +++ b/source4/ntvfs/posix/pvfs_qfileinfo.c @@ -148,17 +148,7 @@ static NTSTATUS pvfs_map_fileinfo(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, case RAW_FILEINFO_STREAM_INFO: case RAW_FILEINFO_STREAM_INFORMATION: - info->stream_info.out.num_streams = 1; - info->stream_info.out.streams = talloc_array_p(mem_ctx, - struct stream_struct, 1); - if (!info->stream_info.out.streams) { - return NT_STATUS_NO_MEMORY; - } - info->stream_info.out.streams[0].size = name->st.st_size; - info->stream_info.out.streams[0].alloc_size = name->dos.alloc_size; - info->stream_info.out.streams[0].stream_name.s = - talloc_strdup(info->stream_info.out.streams, "::$DATA"); - return NT_STATUS_OK; + return pvfs_stream_information(pvfs, mem_ctx, name, fd, &info->stream_info.out); case RAW_FILEINFO_COMPRESSION_INFO: case RAW_FILEINFO_COMPRESSION_INFORMATION: @@ -219,12 +209,12 @@ NTSTATUS pvfs_qpathinfo(struct ntvfs_module_context *ntvfs, NTSTATUS status; /* resolve the cifs name to a posix name */ - status = pvfs_resolve_name(pvfs, req, info->generic.in.fname, 0, &name); + status = pvfs_resolve_name(pvfs, req, info->generic.in.fname, PVFS_RESOLVE_STREAMS, &name); if (!NT_STATUS_IS_OK(status)) { return status; } - if (!name->exists) { + if (!name->stream_exists) { return NT_STATUS_OBJECT_NAME_NOT_FOUND; } diff --git a/source4/ntvfs/posix/pvfs_read.c b/source4/ntvfs/posix/pvfs_read.c index 0f7d3b4b0c..793a97ba62 100644 --- a/source4/ntvfs/posix/pvfs_read.c +++ b/source4/ntvfs/posix/pvfs_read.c @@ -71,10 +71,15 @@ NTSTATUS pvfs_read(struct ntvfs_module_context *ntvfs, return status; } - ret = pread(f->handle->fd, - rd->readx.out.data, - maxcnt, - rd->readx.in.offset); + if (f->handle->name->stream_name) { + ret = pvfs_stream_read(pvfs, f->handle, + rd->readx.out.data, maxcnt, rd->readx.in.offset); + } else { + ret = pread(f->handle->fd, + rd->readx.out.data, + maxcnt, + rd->readx.in.offset); + } if (ret == -1) { return pvfs_map_errno(pvfs, errno); } diff --git a/source4/ntvfs/posix/pvfs_rename.c b/source4/ntvfs/posix/pvfs_rename.c index efaf63ba9d..cba9cace59 100644 --- a/source4/ntvfs/posix/pvfs_rename.c +++ b/source4/ntvfs/posix/pvfs_rename.c @@ -317,16 +317,22 @@ static NTSTATUS pvfs_rename_nt(struct ntvfs_module_context *ntvfs, } /* resolve the cifs name to a posix name */ - status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.old_name, 0, &name1); + status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.old_name, + PVFS_RESOLVE_WILDCARD, &name1); if (!NT_STATUS_IS_OK(status)) { return status; } - status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.new_name, 0, &name2); + status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.new_name, + PVFS_RESOLVE_WILDCARD, &name2); if (!NT_STATUS_IS_OK(status)) { return status; } + if (name1->has_wildcard || name2->has_wildcard) { + return NT_STATUS_OBJECT_PATH_SYNTAX_BAD; + } + if (!name1->exists) { return NT_STATUS_OBJECT_NAME_NOT_FOUND; } diff --git a/source4/ntvfs/posix/pvfs_resolve.c b/source4/ntvfs/posix/pvfs_resolve.c index 576b0d9db4..302ccdae60 100644 --- a/source4/ntvfs/posix/pvfs_resolve.c +++ b/source4/ntvfs/posix/pvfs_resolve.c @@ -181,6 +181,37 @@ static NTSTATUS pvfs_case_search(struct pvfs_state *pvfs, struct pvfs_filename * return NT_STATUS_OK; } +/* + parse a alternate data stream name +*/ +static NTSTATUS parse_stream_name(struct pvfs_filename *name, const char *s) +{ + char *p; + name->stream_name = talloc_strdup(name, s+1); + if (name->stream_name == NULL) { + return NT_STATUS_NO_MEMORY; + } + p = strchr_m(name->stream_name, ':'); + if (p == NULL) { + name->stream_id = pvfs_name_hash(name->stream_name, + strlen(name->stream_name)); + return NT_STATUS_OK; + } + if (StrCaseCmp(p, ":$DATA") != 0) { + return NT_STATUS_OBJECT_NAME_INVALID; + } + *p = 0; + if (strcmp(name->stream_name, "") == 0) { + name->stream_name = NULL; + name->stream_id = 0; + } else { + name->stream_id = pvfs_name_hash(name->stream_name, + strlen(name->stream_name)); + } + + return NT_STATUS_OK; +} + /* convert a CIFS pathname to a unix pathname. Note that this does NOT @@ -194,9 +225,11 @@ static NTSTATUS pvfs_unix_path(struct pvfs_state *pvfs, const char *cifs_name, uint_t flags, struct pvfs_filename *name) { char *ret, *p, *p_start; + NTSTATUS status; name->original_name = talloc_strdup(name, cifs_name); name->stream_name = NULL; + name->stream_id = 0; name->has_wildcard = False; while (*cifs_name == '\\') { @@ -242,9 +275,12 @@ static NTSTATUS pvfs_unix_path(struct pvfs_state *pvfs, const char *cifs_name, if (!(flags & PVFS_RESOLVE_STREAMS)) { return NT_STATUS_ILLEGAL_CHARACTER; } - name->stream_name = talloc_strdup(name, p+1); - if (name->stream_name == NULL) { - return NT_STATUS_NO_MEMORY; + if (name->has_wildcard) { + return NT_STATUS_ILLEGAL_CHARACTER; + } + status = parse_stream_name(name, p); + if (!NT_STATUS_IS_OK(status)) { + return status; } *p-- = 0; break; @@ -420,6 +456,7 @@ NTSTATUS pvfs_resolve_name(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, } (*name)->exists = False; + (*name)->stream_exists = False; /* do the basic conversion to a unix formatted path, also checking for allowable characters */ @@ -485,9 +522,11 @@ NTSTATUS pvfs_resolve_partial(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, } (*name)->exists = True; + (*name)->stream_exists = True; (*name)->has_wildcard = False; (*name)->original_name = talloc_strdup(*name, fname); (*name)->stream_name = NULL; + (*name)->stream_id = 0; status = pvfs_fill_dos_info(pvfs, *name, -1); diff --git a/source4/ntvfs/posix/pvfs_setfileinfo.c b/source4/ntvfs/posix/pvfs_setfileinfo.c index 7ba64979c3..ede07983b3 100644 --- a/source4/ntvfs/posix/pvfs_setfileinfo.c +++ b/source4/ntvfs/posix/pvfs_setfileinfo.c @@ -300,17 +300,24 @@ NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs, /* possibly change the file size */ if (newstats.st.st_size != h->name->st.st_size) { - int ret; if (h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) { return NT_STATUS_FILE_IS_A_DIRECTORY; } - if (f->access_mask & SA_RIGHT_FILE_WRITE_APPEND) { - ret = ftruncate(h->fd, newstats.st.st_size); + if (h->name->stream_name) { + status = pvfs_stream_truncate(pvfs, h->name, h->fd, newstats.st.st_size); + if (!NT_STATUS_IS_OK(status)) { + return status; + } } else { - ret = truncate(h->name->full_name, newstats.st.st_size); - } - if (ret == -1) { - return pvfs_map_errno(pvfs, errno); + int ret; + if (f->access_mask & SA_RIGHT_FILE_WRITE_APPEND) { + ret = ftruncate(h->fd, newstats.st.st_size); + } else { + ret = truncate(h->name->full_name, newstats.st.st_size); + } + if (ret == -1) { + return pvfs_map_errno(pvfs, errno); + } } } @@ -461,7 +468,12 @@ NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs, /* possibly change the file size */ if (newstats.st.st_size != name->st.st_size) { - if (truncate(name->full_name, newstats.st.st_size) == -1) { + if (name->stream_name) { + status = pvfs_stream_truncate(pvfs, name, -1, newstats.st.st_size); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } else if (truncate(name->full_name, newstats.st.st_size) == -1) { return pvfs_map_errno(pvfs, errno); } } diff --git a/source4/ntvfs/posix/pvfs_shortname.c b/source4/ntvfs/posix/pvfs_shortname.c index 3627820bb0..c0fc2fb136 100644 --- a/source4/ntvfs/posix/pvfs_shortname.c +++ b/source4/ntvfs/posix/pvfs_shortname.c @@ -36,11 +36,6 @@ for simplicity, we only allow ascii characters in 8.3 names */ - /* hash alghorithm changed to FNV1 by idra@samba.org (Simo Sorce). - * see http://www.isthe.com/chongo/tech/comp/fnv/index.html for a - * discussion on Fowler / Noll / Vo (FNV) Hash by one of it's authors - */ - /* =============================================================================== NOTE NOTE NOTE!!! @@ -83,10 +78,6 @@ #define DEFAULT_MANGLE_PREFIX 4 -#define FNV1_PRIME 0x01000193 -/*the following number is a fnv1 of the string: idra@samba.org 2002 */ -#define FNV1_INIT 0xa6b93095 - #define MANGLE_BASECHARS "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" #define FLAG_CHECK(c, flag) (ctx->char_flags[(uint8_t)(c)] & (flag)) @@ -105,19 +96,7 @@ static const char *reserved_names[] = static uint32_t mangle_hash(struct pvfs_mangle_context *ctx, const char *key, size_t length) { - uint32_t value = FNV1_INIT; - codepoint_t c; - size_t c_size; - - while (*key && length--) { - c = next_codepoint(key, &c_size); - c = toupper_w(c); - value *= (uint32_t)FNV1_PRIME; - value ^= (uint32_t)c; - key += c_size; - } - - return (value % ctx->mangle_modulus); + return pvfs_name_hash(key, length) % ctx->mangle_modulus; } /* diff --git a/source4/ntvfs/posix/pvfs_streams.c b/source4/ntvfs/posix/pvfs_streams.c index b71ff640e5..7edf111fc9 100644 --- a/source4/ntvfs/posix/pvfs_streams.c +++ b/source4/ntvfs/posix/pvfs_streams.c @@ -25,3 +25,325 @@ #include "vfs_posix.h" #include "librpc/gen_ndr/ndr_xattr.h" + +/* + return the list of file streams for RAW_FILEINFO_STREAM_INFORMATION +*/ +NTSTATUS pvfs_stream_information(struct pvfs_state *pvfs, + TALLOC_CTX *mem_ctx, + struct pvfs_filename *name, int fd, + struct stream_information *info) +{ + struct xattr_DosStreams *streams; + int i; + NTSTATUS status; + + streams = talloc_p(mem_ctx, struct xattr_DosStreams); + if (streams == NULL) { + return NT_STATUS_NO_MEMORY; + } + + status = pvfs_streams_load(pvfs, name, fd, streams); + if (!NT_STATUS_IS_OK(status)) { + ZERO_STRUCTP(streams); + } + + info->num_streams = streams->num_streams+1; + info->streams = talloc_array_p(mem_ctx, struct stream_struct, info->num_streams); + if (!info->streams) { + return NT_STATUS_NO_MEMORY; + } + + info->streams[0].size = name->st.st_size; + info->streams[0].alloc_size = name->dos.alloc_size; + info->streams[0].stream_name.s = talloc_strdup(info->streams, "::$DATA"); + + for (i=0;i<streams->num_streams;i++) { + info->streams[i+1].size = streams->streams[i].size; + info->streams[i+1].alloc_size = streams->streams[i].alloc_size; + info->streams[i+1].stream_name.s = talloc_asprintf(streams->streams, + ":%s:$DATA", + streams->streams[i].name); + } + + return NT_STATUS_OK; +} + + +/* + fill in the stream information for a name +*/ +NTSTATUS pvfs_stream_info(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd) +{ + struct xattr_DosStreams *streams; + int i; + NTSTATUS status; + + /* the NULL stream always exists */ + if (name->stream_name == NULL) { + name->stream_exists = True; + return NT_STATUS_OK; + } + + streams = talloc_p(name, struct xattr_DosStreams); + if (streams == NULL) { + return NT_STATUS_NO_MEMORY; + } + + status = pvfs_streams_load(pvfs, name, fd, streams); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(streams); + return status; + } + + for (i=0;i<streams->num_streams;i++) { + struct xattr_DosStream *s = &streams->streams[i]; + if (StrCaseCmp(s->name, name->stream_name) == 0) { + name->dos.alloc_size = s->alloc_size; + name->st.st_size = s->size; + name->stream_exists = True; + talloc_free(streams); + return NT_STATUS_OK; + } + } + + talloc_free(streams); + + name->dos.alloc_size = 0; + name->st.st_size = 0; + name->stream_exists = False; + + return NT_STATUS_OK; +} + + +/* + update size information for a stream +*/ +static NTSTATUS pvfs_stream_update_size(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd, + off_t size) +{ + struct xattr_DosStreams *streams; + int i; + NTSTATUS status; + + streams = talloc_p(name, struct xattr_DosStreams); + if (streams == NULL) { + return NT_STATUS_NO_MEMORY; + } + + status = pvfs_streams_load(pvfs, name, fd, streams); + if (!NT_STATUS_IS_OK(status)) { + ZERO_STRUCTP(streams); + } + + for (i=0;i<streams->num_streams;i++) { + struct xattr_DosStream *s = &streams->streams[i]; + if (StrCaseCmp(s->name, name->stream_name) == 0) { + s->size = size; + s->alloc_size = size; + break; + } + } + + if (i == streams->num_streams) { + struct xattr_DosStream *s; + streams->streams = talloc_realloc_p(streams, streams->streams, + struct xattr_DosStream, + streams->num_streams+1); + if (streams->streams == NULL) { + talloc_free(streams); + return NT_STATUS_NO_MEMORY; + } + streams->num_streams++; + s = &streams->streams[i]; + + s->flags = XATTR_STREAM_FLAG_INTERNAL; + s->size = size; + s->alloc_size = size; + s->name = name->stream_name; + } + + status = pvfs_streams_save(pvfs, name, fd, streams); + talloc_free(streams); + + return status; +} + + +/* + create the xattr for a alternate data stream +*/ +NTSTATUS pvfs_stream_create(struct pvfs_state *pvfs, + struct pvfs_filename *name, + int fd) +{ + NTSTATUS status; + status = pvfs_xattr_create(pvfs, name->full_name, fd, + XATTR_DOSSTREAM_PREFIX, name->stream_name); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + return pvfs_stream_update_size(pvfs, name, fd, 0); +} + +/* + delete the xattr for a alternate data stream +*/ +NTSTATUS pvfs_stream_delete(struct pvfs_state *pvfs, + struct pvfs_filename *name, + int fd) +{ + NTSTATUS status; + struct xattr_DosStreams *streams; + int i; + + status = pvfs_xattr_delete(pvfs, name->full_name, fd, + XATTR_DOSSTREAM_PREFIX, name->stream_name); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + streams = talloc_p(name, struct xattr_DosStreams); + if (streams == NULL) { + return NT_STATUS_NO_MEMORY; + } + + status = pvfs_streams_load(pvfs, name, fd, streams); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(streams); + return status; + } + + for (i=0;i<streams->num_streams;i++) { + struct xattr_DosStream *s = &streams->streams[i]; + if (StrCaseCmp(s->name, name->stream_name) == 0) { + memmove(s, s+1, (streams->num_streams - (i+1)) * sizeof(*s)); + streams->num_streams--; + break; + } + } + + status = pvfs_streams_save(pvfs, name, fd, streams); + talloc_free(streams); + + return status; +} + +/* + the equvalent of pread() on a stream +*/ +ssize_t pvfs_stream_read(struct pvfs_state *pvfs, + struct pvfs_file_handle *h, void *data, size_t count, off_t offset) +{ + NTSTATUS status; + DATA_BLOB blob; + if (count == 0) { + return 0; + } + status = pvfs_xattr_load(pvfs, h, h->name->full_name, h->fd, XATTR_DOSSTREAM_PREFIX, + h->name->stream_name, offset+count, &blob); + if (!NT_STATUS_IS_OK(status)) { + errno = EIO; + return -1; + } + if (offset >= blob.length) { + data_blob_free(&blob); + return 0; + } + if (count > blob.length - offset) { + count = blob.length - offset; + } + memcpy(data, blob.data + offset, count); + data_blob_free(&blob); + return count; +} + + +/* + the equvalent of pwrite() on a stream +*/ +ssize_t pvfs_stream_write(struct pvfs_state *pvfs, + struct pvfs_file_handle *h, const void *data, size_t count, off_t offset) +{ + NTSTATUS status; + DATA_BLOB blob; + if (count == 0) { + return 0; + } + /* we have to load the existing stream, then modify, then save */ + status = pvfs_xattr_load(pvfs, h, h->name->full_name, h->fd, XATTR_DOSSTREAM_PREFIX, + h->name->stream_name, offset+count, &blob); + if (!NT_STATUS_IS_OK(status)) { + blob = data_blob(NULL, 0); + } + if (count+offset > blob.length) { + blob.data = talloc_realloc(blob.data, blob.data, count+offset); + if (blob.data == NULL) { + errno = ENOMEM; + return -1; + } + if (offset > blob.length) { + memset(blob.data+blob.length, 0, offset - blob.length); + } + blob.length = count+offset; + } + memcpy(blob.data + offset, data, count); + + status = pvfs_xattr_save(pvfs, h->name->full_name, h->fd, XATTR_DOSSTREAM_PREFIX, + h->name->stream_name, &blob); + if (!NT_STATUS_IS_OK(status)) { + data_blob_free(&blob); + /* getting this error mapping right is probably + not worth it */ + errno = ENOSPC; + return -1; + } + + status = pvfs_stream_update_size(pvfs, h->name, h->fd, blob.length); + + data_blob_free(&blob); + + if (!NT_STATUS_IS_OK(status)) { + errno = EIO; + return -1; + } + + return count; +} + +/* + the equvalent of truncate() on a stream +*/ +NTSTATUS pvfs_stream_truncate(struct pvfs_state *pvfs, + struct pvfs_filename *name, int fd, off_t length) +{ + NTSTATUS status; + DATA_BLOB blob; + /* we have to load the existing stream, then modify, then save */ + status = pvfs_xattr_load(pvfs, name, name->full_name, fd, XATTR_DOSSTREAM_PREFIX, + name->stream_name, length, &blob); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (length <= blob.length) { + blob.length = length; + } else if (length > blob.length) { + blob.data = talloc_realloc(blob.data, blob.data, length); + if (blob.data == NULL) { + return NT_STATUS_NO_MEMORY; + } + memset(blob.data+blob.length, 0, length - blob.length); + blob.length = length; + } + + status = pvfs_xattr_save(pvfs, name->full_name, fd, XATTR_DOSSTREAM_PREFIX, + name->stream_name, &blob); + data_blob_free(&blob); + + if (NT_STATUS_IS_OK(status)) { + status = pvfs_stream_update_size(pvfs, name, fd, blob.length); + } + + return status; +} diff --git a/source4/ntvfs/posix/pvfs_unlink.c b/source4/ntvfs/posix/pvfs_unlink.c index 8dc9f60ad8..434577a862 100644 --- a/source4/ntvfs/posix/pvfs_unlink.c +++ b/source4/ntvfs/posix/pvfs_unlink.c @@ -25,6 +25,33 @@ /* + unlink a stream + */ +static NTSTATUS pvfs_unlink_stream(struct pvfs_state *pvfs, struct pvfs_filename *name, + uint16_t attrib) +{ + NTSTATUS status; + + if (!name->stream_exists) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + /* make sure its matches the given attributes */ + status = pvfs_match_attrib(pvfs, name, attrib, 0); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = pvfs_can_delete(pvfs, name); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return pvfs_stream_delete(pvfs, name, -1); +} + + +/* unlink one file */ static NTSTATUS pvfs_unlink_one(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, @@ -86,7 +113,7 @@ NTSTATUS pvfs_unlink(struct ntvfs_module_context *ntvfs, /* resolve the cifs name to a posix name */ status = pvfs_resolve_name(pvfs, req, unl->in.pattern, - PVFS_RESOLVE_WILDCARD, &name); + PVFS_RESOLVE_WILDCARD | PVFS_RESOLVE_STREAMS, &name); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -100,6 +127,10 @@ NTSTATUS pvfs_unlink(struct ntvfs_module_context *ntvfs, return NT_STATUS_FILE_IS_A_DIRECTORY; } + if (name->stream_name) { + return pvfs_unlink_stream(pvfs, name, unl->in.attrib); + } + /* get list of matching files */ status = pvfs_list_start(pvfs, name, req, &dir); if (!NT_STATUS_IS_OK(status)) { diff --git a/source4/ntvfs/posix/pvfs_util.c b/source4/ntvfs/posix/pvfs_util.c index 6503769013..9f617d39d4 100644 --- a/source4/ntvfs/posix/pvfs_util.c +++ b/source4/ntvfs/posix/pvfs_util.c @@ -160,3 +160,30 @@ NTSTATUS pvfs_copy_file(struct pvfs_state *pvfs, return NT_STATUS_OK; } + + +/* + hash a string of the specified length. The string does not need to be + null terminated + + hash alghorithm changed to FNV1 by idra@samba.org (Simo Sorce). + see http://www.isthe.com/chongo/tech/comp/fnv/index.html for a + discussion on Fowler / Noll / Vo (FNV) Hash by one of it's authors +*/ +uint32_t pvfs_name_hash(const char *key, size_t length) +{ + const uint32_t fnv1_prime = 0x01000193; + const uint32_t fnv1_init = 0xa6b93095; + uint32_t value = fnv1_init; + + while (*key && length--) { + size_t c_size; + codepoint_t c = next_codepoint(key, &c_size); + c = toupper_w(c); + value *= fnv1_prime; + value ^= (uint32_t)c; + key += c_size; + } + + return value; +} diff --git a/source4/ntvfs/posix/pvfs_write.c b/source4/ntvfs/posix/pvfs_write.c index c24de08a13..3f6e8d908a 100644 --- a/source4/ntvfs/posix/pvfs_write.c +++ b/source4/ntvfs/posix/pvfs_write.c @@ -60,10 +60,18 @@ NTSTATUS pvfs_write(struct ntvfs_module_context *ntvfs, return status; } - ret = pwrite(f->handle->fd, - wr->writex.in.data, - wr->writex.in.count, - wr->writex.in.offset); + if (f->handle->name->stream_name) { + ret = pvfs_stream_write(pvfs, + f->handle, + wr->writex.in.data, + wr->writex.in.count, + wr->writex.in.offset); + } else { + ret = pwrite(f->handle->fd, + wr->writex.in.data, + wr->writex.in.count, + wr->writex.in.offset); + } if (ret == -1) { if (errno == EFBIG) { return NT_STATUS_INVALID_PARAMETER; diff --git a/source4/ntvfs/posix/pvfs_xattr.c b/source4/ntvfs/posix/pvfs_xattr.c index 269a2cc863..ff0c0f5116 100644 --- a/source4/ntvfs/posix/pvfs_xattr.c +++ b/source4/ntvfs/posix/pvfs_xattr.c @@ -100,6 +100,31 @@ static NTSTATUS push_xattr_blob(struct pvfs_state *pvfs, #endif } + +/* + delete a xattr +*/ +static NTSTATUS delete_xattr(struct pvfs_state *pvfs, const char *attr_name, + const char *fname, int fd) +{ +#if HAVE_XATTR_SUPPORT + int ret; + + if (fd != -1) { + ret = fremovexattr(fd, attr_name); + } else { + ret = removexattr(fname, attr_name); + } + if (ret == -1) { + return pvfs_map_errno(pvfs, errno); + } + + return NT_STATUS_OK; +#else + return NT_STATUS_NOT_SUPPORTED; +#endif +} + /* load a NDR structure from a xattr */ @@ -159,6 +184,12 @@ NTSTATUS pvfs_dosattrib_load(struct pvfs_state *pvfs, struct pvfs_filename *name TALLOC_CTX *mem_ctx = talloc(name, 0); struct xattr_DosInfo1 *info1; + 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; } @@ -209,9 +240,11 @@ NTSTATUS pvfs_dosattrib_load(struct pvfs_state *pvfs, struct pvfs_filename *name talloc_free(mem_ctx); return NT_STATUS_INVALID_LEVEL; } - talloc_free(mem_ctx); - return NT_STATUS_OK; + + status = pvfs_stream_info(pvfs, name, fd); + + return status; } @@ -313,3 +346,81 @@ NTSTATUS pvfs_streams_save(struct pvfs_state *pvfs, struct pvfs_filename *name, streams, (ndr_push_flags_fn_t)ndr_push_xattr_DosStreams); } + +/* + 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; +} diff --git a/source4/ntvfs/posix/vfs_posix.h b/source4/ntvfs/posix/vfs_posix.h index 645d293a6d..09d4bce085 100644 --- a/source4/ntvfs/posix/vfs_posix.h +++ b/source4/ntvfs/posix/vfs_posix.h @@ -76,9 +76,11 @@ struct pvfs_dos_fileinfo { struct pvfs_filename { const char *original_name; char *full_name; - const char *stream_name; + const char *stream_name; /* does not include :$DATA suffix */ + uint32_t stream_id; /* this uses a hash, so is probabilistic */ BOOL has_wildcard; - BOOL exists; + BOOL exists; /* true if the base filename exists */ + BOOL stream_exists; /* true if the stream exists */ struct stat st; struct pvfs_dos_fileinfo dos; }; @@ -96,8 +98,11 @@ struct pvfs_file_handle { struct pvfs_filename *name; - /* a unique file key to be used for file locking */ - DATA_BLOB locking_key; + /* a unique file key to be used for open file locking */ + DATA_BLOB odb_locking_key; + + /* a unique file key to be used for byte range locking */ + DATA_BLOB brl_locking_key; uint32_t create_options; diff --git a/source4/torture/raw/streams.c b/source4/torture/raw/streams.c index 239955fd93..7bc80b8502 100644 --- a/source4/torture/raw/streams.c +++ b/source4/torture/raw/streams.c @@ -108,12 +108,10 @@ static BOOL test_stream_io(struct smbcli_state *cli, TALLOC_CTX *mem_ctx) io.generic.level = RAW_OPEN_NTCREATEX; io.ntcreatex.in.root_fid = 0; io.ntcreatex.in.flags = 0; - io.ntcreatex.in.access_mask = SEC_RIGHT_MAXIMUM_ALLOWED; + io.ntcreatex.in.access_mask = SA_RIGHT_FILE_WRITE_DATA; io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; - io.ntcreatex.in.share_access = - NTCREATEX_SHARE_ACCESS_READ | - NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.share_access = 0; io.ntcreatex.in.alloc_size = 0; io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; @@ -131,6 +129,13 @@ static BOOL test_stream_io(struct smbcli_state *cli, TALLOC_CTX *mem_ctx) ret &= check_stream(cli, mem_ctx, fname, "Stream One", NULL); + printf("check that open of base file is allowed\n"); + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.fname = fname; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + smbcli_close(cli->tree, io.ntcreatex.out.fnum); + printf("writing to stream\n"); retsize = smbcli_write(cli->tree, fnum, 0, "test data", 0, 9); CHECK_VALUE(retsize, 9); @@ -140,6 +145,7 @@ static BOOL test_stream_io(struct smbcli_state *cli, TALLOC_CTX *mem_ctx) ret &= check_stream(cli, mem_ctx, fname, "Stream One", "test data"); io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatex.in.fname = sname1; status = smb_raw_open(cli->tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); fnum = io.ntcreatex.out.fnum; |