summaryrefslogtreecommitdiff
path: root/source4/ntvfs
diff options
context:
space:
mode:
Diffstat (limited to 'source4/ntvfs')
-rw-r--r--source4/ntvfs/cifs/vfs_cifs.c10
-rw-r--r--source4/ntvfs/ntvfs_generic.c130
-rw-r--r--source4/ntvfs/posix/pvfs_acl.c2
-rw-r--r--source4/ntvfs/posix/pvfs_fileinfo.c7
-rw-r--r--source4/ntvfs/posix/pvfs_flush.c1
-rw-r--r--source4/ntvfs/posix/pvfs_open.c100
-rw-r--r--source4/ntvfs/posix/pvfs_qfileinfo.c9
-rw-r--r--source4/ntvfs/posix/pvfs_read.c8
-rw-r--r--source4/ntvfs/posix/pvfs_resolve.c8
-rw-r--r--source4/ntvfs/posix/pvfs_streams.c20
10 files changed, 237 insertions, 58 deletions
diff --git a/source4/ntvfs/cifs/vfs_cifs.c b/source4/ntvfs/cifs/vfs_cifs.c
index 2b61268733..844fa11cc5 100644
--- a/source4/ntvfs/cifs/vfs_cifs.c
+++ b/source4/ntvfs/cifs/vfs_cifs.c
@@ -769,6 +769,7 @@ static NTSTATUS cvfs_close(struct ntvfs_module_context *ntvfs,
struct cvfs_private *private = ntvfs->private_data;
struct smbcli_request *c_req;
struct cvfs_file *f;
+ union smb_close io2;
SETUP_PID;
@@ -776,6 +777,15 @@ static NTSTATUS cvfs_close(struct ntvfs_module_context *ntvfs,
private->map_generic) {
return ntvfs_map_close(ntvfs, req, io);
}
+
+ if (io->generic.level == RAW_CLOSE_GENERIC) {
+ ZERO_STRUCT(io2);
+ io2.close.level = RAW_CLOSE_CLOSE;
+ io2.close.in.file = io->generic.in.file;
+ io2.close.in.write_time = io->generic.in.write_time;
+ io = &io2;
+ }
+
SETUP_FILE_HERE(f);
/* Note, we aren't free-ing f, or it's h here. Should we?
even if file-close fails, we'll remove it from the list,
diff --git a/source4/ntvfs/ntvfs_generic.c b/source4/ntvfs/ntvfs_generic.c
index a706e621c9..06d89a717b 100644
--- a/source4/ntvfs/ntvfs_generic.c
+++ b/source4/ntvfs/ntvfs_generic.c
@@ -522,6 +522,19 @@ NTSTATUS ntvfs_map_open(struct ntvfs_module_context *ntvfs,
io2->generic.in.fname = io->smb2.in.fname;
io2->generic.in.sec_desc = NULL;
io2->generic.in.ea_list = NULL;
+
+ /* we need to check these bits before we check the private mask */
+ if (io2->generic.in.create_options & NTCREATEX_OPTIONS_NOT_SUPPORTED_MASK) {
+ status = NT_STATUS_NOT_SUPPORTED;
+ break;
+ }
+
+ /* we use a couple of bits of the create options internally */
+ if (io2->generic.in.create_options & NTCREATEX_OPTIONS_PRIVATE_MASK) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+
status = ntvfs->ops->open(ntvfs, req, io2);
break;
@@ -1014,7 +1027,7 @@ NTSTATUS ntvfs_map_lock(struct ntvfs_module_context *ntvfs,
case RAW_LOCK_SMB2: {
/* this is only approximate! We need to change the
generic structure to fix this properly */
- int i;
+ int i, j;
if (lck->smb2.in.lock_count < 1) {
return NT_STATUS_INVALID_PARAMETER;
}
@@ -1031,30 +1044,36 @@ NTSTATUS ntvfs_map_lock(struct ntvfs_module_context *ntvfs,
return NT_STATUS_NO_MEMORY;
}
for (i=0;i<lck->smb2.in.lock_count;i++) {
- if (lck->smb2.in.locks[i].flags & SMB2_LOCK_FLAG_UNLOCK) {
- int j = lck2->generic.in.ulock_cnt;
- lck2->generic.in.ulock_cnt++;
- lck2->generic.in.locks[j].pid = 0;
- lck2->generic.in.locks[j].offset = lck->smb2.in.locks[i].offset;
- lck2->generic.in.locks[j].count = lck->smb2.in.locks[i].length;
- lck2->generic.in.locks[j].pid = 0;
+ if (!(lck->smb2.in.locks[i].flags & SMB2_LOCK_FLAG_UNLOCK)) {
+ break;
+ }
+ j = lck2->generic.in.ulock_cnt;
+ if (lck->smb2.in.locks[i].flags &
+ (SMB2_LOCK_FLAG_SHARED|SMB2_LOCK_FLAG_EXCLUSIVE)) {
+ return NT_STATUS_INVALID_PARAMETER;
}
+ lck2->generic.in.ulock_cnt++;
+ lck2->generic.in.locks[j].pid = 0;
+ lck2->generic.in.locks[j].offset = lck->smb2.in.locks[i].offset;
+ lck2->generic.in.locks[j].count = lck->smb2.in.locks[i].length;
+ lck2->generic.in.locks[j].pid = 0;
}
- for (i=0;i<lck->smb2.in.lock_count;i++) {
- if (!(lck->smb2.in.locks[i].flags & SMB2_LOCK_FLAG_UNLOCK)) {
- int j = lck2->generic.in.ulock_cnt +
- lck2->generic.in.lock_cnt;
- lck2->generic.in.lock_cnt++;
- lck2->generic.in.locks[j].pid = 0;
- lck2->generic.in.locks[j].offset = lck->smb2.in.locks[i].offset;
- lck2->generic.in.locks[j].count = lck->smb2.in.locks[i].length;
- lck2->generic.in.locks[j].pid = 0;
- if (!(lck->smb2.in.locks[i].flags & SMB2_LOCK_FLAG_EXCLUSIVE)) {
- lck2->generic.in.mode = LOCKING_ANDX_SHARED_LOCK;
- }
- if (lck->smb2.in.locks[i].flags & SMB2_LOCK_FLAG_FAIL_IMMEDIATELY) {
- lck2->generic.in.timeout = 0;
- }
+ for (;i<lck->smb2.in.lock_count;i++) {
+ if (lck->smb2.in.locks[i].flags & SMB2_LOCK_FLAG_UNLOCK) {
+ /* w2008 requires unlocks to come first */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ j = lck2->generic.in.ulock_cnt + lck2->generic.in.lock_cnt;
+ lck2->generic.in.lock_cnt++;
+ lck2->generic.in.locks[j].pid = 0;
+ lck2->generic.in.locks[j].offset = lck->smb2.in.locks[i].offset;
+ lck2->generic.in.locks[j].count = lck->smb2.in.locks[i].length;
+ lck2->generic.in.locks[j].pid = 0;
+ if (!(lck->smb2.in.locks[i].flags & SMB2_LOCK_FLAG_EXCLUSIVE)) {
+ lck2->generic.in.mode = LOCKING_ANDX_SHARED_LOCK;
+ }
+ if (lck->smb2.in.locks[i].flags & SMB2_LOCK_FLAG_FAIL_IMMEDIATELY) {
+ lck2->generic.in.timeout = 0;
}
}
/* initialize output value */
@@ -1367,7 +1386,7 @@ NTSTATUS ntvfs_map_read(struct ntvfs_module_context *ntvfs,
case RAW_READ_SMB2:
rd2->readx.in.file.ntvfs= rd->smb2.in.file.ntvfs;
rd2->readx.in.offset = rd->smb2.in.offset;
- rd2->readx.in.mincnt = rd->smb2.in.length;
+ rd2->readx.in.mincnt = rd->smb2.in.min_count;
rd2->readx.in.maxcnt = rd->smb2.in.length;
rd2->readx.in.remaining = 0;
rd2->readx.out.data = rd->smb2.out.data.data;
@@ -1383,11 +1402,42 @@ done:
/*
NTVFS close generic to any mapper
*/
+static NTSTATUS ntvfs_map_close_finish(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_close *cl,
+ union smb_close *cl2,
+ NTSTATUS status)
+{
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ switch (cl->generic.level) {
+ case RAW_CLOSE_SMB2:
+ cl->smb2.out.flags = cl2->generic.out.flags;
+ cl->smb2.out._pad = 0;
+ cl->smb2.out.create_time = cl2->generic.out.create_time;
+ cl->smb2.out.access_time = cl2->generic.out.access_time;
+ cl->smb2.out.write_time = cl2->generic.out.write_time;
+ cl->smb2.out.change_time = cl2->generic.out.change_time;
+ cl->smb2.out.alloc_size = cl2->generic.out.alloc_size;
+ cl->smb2.out.size = cl2->generic.out.size;
+ cl->smb2.out.file_attr = cl2->generic.out.file_attr;
+ break;
+ default:
+ break;
+ }
+
+ return status;
+}
+
+/*
+ NTVFS close generic to any mapper
+*/
NTSTATUS ntvfs_map_close(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req,
union smb_close *cl)
{
union smb_close *cl2;
+ NTSTATUS status;
cl2 = talloc(req, union smb_close);
if (cl2 == NULL) {
@@ -1395,30 +1445,38 @@ NTSTATUS ntvfs_map_close(struct ntvfs_module_context *ntvfs,
}
switch (cl->generic.level) {
- case RAW_CLOSE_CLOSE:
+ case RAW_CLOSE_GENERIC:
return NT_STATUS_INVALID_LEVEL;
+ case RAW_CLOSE_CLOSE:
+ cl2->generic.level = RAW_CLOSE_GENERIC;
+ cl2->generic.in.file = cl->close.in.file;
+ cl2->generic.in.write_time = cl->close.in.write_time;
+ cl2->generic.in.flags = 0;
+ break;
+
case RAW_CLOSE_SPLCLOSE:
- cl2->generic.level = RAW_CLOSE_CLOSE;
- cl2->generic.in.file.ntvfs = cl->splclose.in.file.ntvfs;
+ cl2->generic.level = RAW_CLOSE_GENERIC;
+ cl2->generic.in.file = cl->splclose.in.file;
cl2->generic.in.write_time = 0;
+ cl2->generic.in.flags = 0;
break;
case RAW_CLOSE_SMB2:
- cl2->generic.level = RAW_CLOSE_CLOSE;
- cl2->generic.in.file.ntvfs = cl->smb2.in.file.ntvfs;
+ cl2->generic.level = RAW_CLOSE_GENERIC;
+ cl2->generic.in.file = cl->smb2.in.file;
cl2->generic.in.write_time = 0;
- /* SMB2 Close has output parameter, but we just zero them */
- ZERO_STRUCT(cl->smb2.out);
+ cl2->generic.in.flags = cl->smb2.in.flags;
break;
}
- /*
- * we don't need to call ntvfs_map_async_setup() here,
- * as close() doesn't have any output fields
- */
+ status = ntvfs_map_async_setup(ntvfs, req, cl, cl2,
+ (second_stage_t)ntvfs_map_close_finish);
+ NT_STATUS_NOT_OK_RETURN(status);
- return ntvfs->ops->close(ntvfs, req, cl2);
+ status = ntvfs->ops->close(ntvfs, req, cl2);
+
+ return ntvfs_map_async_finish(req, status);
}
/*
diff --git a/source4/ntvfs/posix/pvfs_acl.c b/source4/ntvfs/posix/pvfs_acl.c
index f1e469f790..507c22f050 100644
--- a/source4/ntvfs/posix/pvfs_acl.c
+++ b/source4/ntvfs/posix/pvfs_acl.c
@@ -457,7 +457,7 @@ NTSTATUS pvfs_access_check_unix(struct pvfs_state *pvfs,
}
if (uid != 0 && (*access_mask & SEC_FLAG_SYSTEM_SECURITY)) {
- return NT_STATUS_PRIVILEGE_NOT_HELD;
+ return NT_STATUS_ACCESS_DENIED;
}
if (*access_mask & ~max_bits) {
diff --git a/source4/ntvfs/posix/pvfs_fileinfo.c b/source4/ntvfs/posix/pvfs_fileinfo.c
index 4c383ed45d..04f6ad78d0 100644
--- a/source4/ntvfs/posix/pvfs_fileinfo.c
+++ b/source4/ntvfs/posix/pvfs_fileinfo.c
@@ -58,6 +58,8 @@ NTSTATUS pvfs_fill_dos_info(struct pvfs_state *pvfs, struct pvfs_filename *name,
if (S_ISDIR(name->st.st_mode)) {
name->st.st_size = 0;
name->st.st_nlink = 1;
+ } else if (name->stream_id == 0) {
+ name->stream_name = NULL;
}
/* for now just use the simple samba mapping */
@@ -75,6 +77,11 @@ NTSTATUS pvfs_fill_dos_info(struct pvfs_state *pvfs, struct pvfs_filename *name,
name->dos.alloc_size = pvfs_round_alloc_size(pvfs, name->st.st_size);
name->dos.nlink = name->st.st_nlink;
name->dos.ea_size = 4;
+ if (pvfs->ntvfs->ctx->protocol == PROTOCOL_SMB2) {
+ /* SMB2 represents a null EA with zero bytes */
+ name->dos.ea_size = 0;
+ }
+
name->dos.file_id = (((uint64_t)name->st.st_dev)<<32) | name->st.st_ino;
name->dos.flags = 0;
diff --git a/source4/ntvfs/posix/pvfs_flush.c b/source4/ntvfs/posix/pvfs_flush.c
index 61e73cedba..6e09c1f34a 100644
--- a/source4/ntvfs/posix/pvfs_flush.c
+++ b/source4/ntvfs/posix/pvfs_flush.c
@@ -54,6 +54,7 @@ NTSTATUS pvfs_flush(struct ntvfs_module_context *ntvfs,
return NT_STATUS_INVALID_HANDLE;
}
pvfs_flush_file(pvfs, f);
+ io->smb2.out.reserved = 0;
return NT_STATUS_OK;
case RAW_FLUSH_ALL:
diff --git a/source4/ntvfs/posix/pvfs_open.c b/source4/ntvfs/posix/pvfs_open.c
index 67937324cc..49710806c7 100644
--- a/source4/ntvfs/posix/pvfs_open.c
+++ b/source4/ntvfs/posix/pvfs_open.c
@@ -182,12 +182,19 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
bool del_on_close;
uint32_t create_options;
uint32_t share_access;
+ bool forced;
create_options = io->generic.in.create_options;
share_access = io->generic.in.share_access;
+ forced = (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)?true:false;
+
if (name->stream_name) {
- return NT_STATUS_NOT_A_DIRECTORY;
+ if (forced) {
+ return NT_STATUS_NOT_A_DIRECTORY;
+ } else {
+ return NT_STATUS_FILE_IS_A_DIRECTORY;
+ }
}
/* if the client says it must be a directory, and it isn't,
@@ -196,6 +203,13 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
return NT_STATUS_NOT_A_DIRECTORY;
}
+ /* found with gentest */
+ if (io->ntcreatex.in.access_mask == SEC_FLAG_MAXIMUM_ALLOWED &&
+ (io->ntcreatex.in.create_options & NTCREATEX_OPTIONS_DIRECTORY) &&
+ (io->ntcreatex.in.create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
switch (io->generic.in.open_disposition) {
case NTCREATEX_DISP_OPEN_IF:
break;
@@ -548,11 +562,19 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
uint32_t oplock_level = OPLOCK_NONE, oplock_granted;
bool allow_level_II_oplock = false;
+ if (io->ntcreatex.in.file_attr & ~FILE_ATTRIBUTE_ALL_MASK) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_ENCRYPTED) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
(create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
return NT_STATUS_CANNOT_DELETE;
}
-
+
status = pvfs_access_check_create(pvfs, req, name, &access_mask);
NT_STATUS_NOT_OK_RETURN(status);
@@ -1110,6 +1132,41 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
return ntvfs_map_open(ntvfs, req, io);
}
+ create_options = io->generic.in.create_options;
+ share_access = io->generic.in.share_access;
+ access_mask = io->generic.in.access_mask;
+
+ if (share_access & ~NTCREATEX_SHARE_ACCESS_MASK) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* some create options are not supported */
+ if (create_options & NTCREATEX_OPTIONS_NOT_SUPPORTED_MASK) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ /* other create options are not allowed */
+ if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
+ !(access_mask & SEC_STD_DELETE)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (access_mask & SEC_MASK_INVALID) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* what does this bit really mean?? */
+ if (req->ctx->protocol == PROTOCOL_SMB2 &&
+ access_mask == SEC_STD_SYNCHRONIZE) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (io->ntcreatex.in.file_attr & (FILE_ATTRIBUTE_DEVICE|
+ FILE_ATTRIBUTE_VOLUME|
+ (~FILE_ATTRIBUTE_ALL_MASK))) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
/* resolve the cifs name to a posix name */
status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
PVFS_RESOLVE_STREAMS, &name);
@@ -1141,16 +1198,6 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
open doesn't match */
io->generic.in.file_attr &= ~FILE_ATTRIBUTE_DIRECTORY;
- create_options = io->generic.in.create_options;
- share_access = io->generic.in.share_access;
- access_mask = io->generic.in.access_mask;
-
- /* certain create options are not allowed */
- if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
- !(access_mask & SEC_STD_DELETE)) {
- return NT_STATUS_INVALID_PARAMETER;
- }
-
flags = 0;
switch (io->generic.in.open_disposition) {
@@ -1467,21 +1514,44 @@ NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
return NT_STATUS_DOS(ERRSRV, ERRerror);
}
- if (io->generic.level != RAW_CLOSE_CLOSE) {
+ if (io->generic.level != RAW_CLOSE_GENERIC) {
return ntvfs_map_close(ntvfs, req, io);
}
- f = pvfs_find_fd(pvfs, req, io->close.in.file.ntvfs);
+ f = pvfs_find_fd(pvfs, req, io->generic.in.file.ntvfs);
if (!f) {
return NT_STATUS_INVALID_HANDLE;
}
- if (!null_time(io->close.in.write_time)) {
+ if (!null_time(io->generic.in.write_time)) {
unix_times.actime = 0;
unix_times.modtime = io->close.in.write_time;
utime(f->handle->name->full_name, &unix_times);
}
+ if (io->generic.in.flags & SMB2_CLOSE_FLAGS_FULL_INFORMATION) {
+ struct pvfs_filename *name;
+ NTSTATUS status;
+ struct pvfs_file_handle *h = f->handle;
+
+ status = pvfs_resolve_name_handle(pvfs, h);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ name = h->name;
+
+ io->generic.out.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION;
+ 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;
+ io->generic.out.change_time = name->dos.change_time;
+ io->generic.out.alloc_size = name->dos.alloc_size;
+ io->generic.out.size = name->st.st_size;
+ io->generic.out.file_attr = name->dos.attrib;
+ } else {
+ ZERO_STRUCT(io->generic.out);
+ }
+
talloc_free(f);
return NT_STATUS_OK;
diff --git a/source4/ntvfs/posix/pvfs_qfileinfo.c b/source4/ntvfs/posix/pvfs_qfileinfo.c
index 6bc21e5e3e..6e3092b744 100644
--- a/source4/ntvfs/posix/pvfs_qfileinfo.c
+++ b/source4/ntvfs/posix/pvfs_qfileinfo.c
@@ -301,7 +301,14 @@ static NTSTATUS pvfs_map_fileinfo(struct pvfs_state *pvfs,
info->all_info2.out.access_mask = 0; /* only set by qfileinfo */
info->all_info2.out.position = 0; /* only set by qfileinfo */
info->all_info2.out.mode = 0; /* only set by qfileinfo */
- info->all_info2.out.fname.s = name->original_name;
+ /* windows wants the full path on disk for this
+ result, but I really don't want to expose that on
+ the wire, so I'll give the path with a share
+ prefix, which is a good approximation */
+ info->all_info2.out.fname.s = talloc_asprintf(req, "\\%s\\%s",
+ pvfs->share_name,
+ name->original_name);
+ NT_STATUS_HAVE_NO_MEMORY(info->all_info2.out.fname.s);
return NT_STATUS_OK;
}
diff --git a/source4/ntvfs/posix/pvfs_read.c b/source4/ntvfs/posix/pvfs_read.c
index 418b7e09fb..a01a8a57e3 100644
--- a/source4/ntvfs/posix/pvfs_read.c
+++ b/source4/ntvfs/posix/pvfs_read.c
@@ -93,6 +93,14 @@ NTSTATUS pvfs_read(struct ntvfs_module_context *ntvfs,
return pvfs_map_errno(pvfs, errno);
}
+ /* only SMB2 honors mincnt */
+ if (req->ctx->protocol == PROTOCOL_SMB2) {
+ if (rd->readx.in.mincnt > ret ||
+ (ret == 0 && maxcnt > 0)) {
+ return NT_STATUS_END_OF_FILE;
+ }
+ }
+
f->handle->position = f->handle->seek_offset = rd->readx.in.offset + ret;
rd->readx.out.nread = ret;
diff --git a/source4/ntvfs/posix/pvfs_resolve.c b/source4/ntvfs/posix/pvfs_resolve.c
index 325bc74f8f..2e97925c49 100644
--- a/source4/ntvfs/posix/pvfs_resolve.c
+++ b/source4/ntvfs/posix/pvfs_resolve.c
@@ -202,7 +202,13 @@ static NTSTATUS parse_stream_name(struct pvfs_filename *name, const char *s)
}
*p = 0;
if (strcmp(name->stream_name, "") == 0) {
- name->stream_name = NULL;
+ /*
+ * we don't set stream_name to NULL, here
+ * as this would be wrong for directories
+ *
+ * pvfs_fill_dos_info() will set it to NULL
+ * if it's not a directory.
+ */
name->stream_id = 0;
} else {
name->stream_id = pvfs_name_hash(name->stream_name,
diff --git a/source4/ntvfs/posix/pvfs_streams.c b/source4/ntvfs/posix/pvfs_streams.c
index 7e6173ef2f..30d7ce2477 100644
--- a/source4/ntvfs/posix/pvfs_streams.c
+++ b/source4/ntvfs/posix/pvfs_streams.c
@@ -36,6 +36,13 @@ NTSTATUS pvfs_stream_information(struct pvfs_state *pvfs,
int i;
NTSTATUS status;
+ /* directories don't have streams */
+ if (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
+ info->num_streams = 0;
+ info->streams = NULL;
+ return NT_STATUS_OK;
+ }
+
streams = talloc(mem_ctx, struct xattr_DosStreams);
if (streams == NULL) {
return NT_STATUS_NO_MEMORY;
@@ -269,9 +276,12 @@ ssize_t pvfs_stream_write(struct pvfs_state *pvfs,
if (count == 0) {
return 0;
}
- if (offset > XATTR_MAX_STREAM_SIZE) {
- errno = ENOSPC;
- return -1;
+
+ if (count+offset > XATTR_MAX_STREAM_SIZE) {
+ if (!pvfs->ea_db || count+offset > XATTR_MAX_STREAM_SIZE_TDB) {
+ errno = ENOSPC;
+ return -1;
+ }
}
/* we have to load the existing stream, then modify, then save */
@@ -325,7 +335,9 @@ NTSTATUS pvfs_stream_truncate(struct pvfs_state *pvfs,
DATA_BLOB blob;
if (length > XATTR_MAX_STREAM_SIZE) {
- return NT_STATUS_DISK_FULL;
+ if (!pvfs->ea_db || length > XATTR_MAX_STREAM_SIZE_TDB) {
+ return NT_STATUS_DISK_FULL;
+ }
}
/* we have to load the existing stream, then modify, then save */