diff options
Diffstat (limited to 'source4/ntvfs/posix/pvfs_streams.c')
-rw-r--r-- | source4/ntvfs/posix/pvfs_streams.c | 199 |
1 files changed, 186 insertions, 13 deletions
diff --git a/source4/ntvfs/posix/pvfs_streams.c b/source4/ntvfs/posix/pvfs_streams.c index 30d7ce2477..381d2033b0 100644 --- a/source4/ntvfs/posix/pvfs_streams.c +++ b/source4/ntvfs/posix/pvfs_streams.c @@ -23,6 +23,57 @@ #include "vfs_posix.h" #include "librpc/gen_ndr/xattr.h" +/* + normalise a stream name, removing a :$DATA suffix if there is one + Note: this returns the existing pointer to the name if the name does + not need normalising + */ +static const char *stream_name_normalise(TALLOC_CTX *ctx, const char *name) +{ + const char *c = strchr_m(name, ':'); + if (c == NULL || strcasecmp_m(c, ":$DATA") != 0) { + return name; + } + return talloc_strndup(ctx, name, c-name); +} + +/* + compare two stream names, taking account of the default $DATA extension + */ +static int stream_name_cmp(const char *name1, const char *name2) +{ + const char *c1, *c2; + int l1, l2, ret; + c1 = strchr_m(name1, ':'); + c2 = strchr_m(name2, ':'); + + /* check the first part is the same */ + l1 = c1?(c1 - name1):strlen(name1); + l2 = c2?(c2 - name2):strlen(name2); + if (l1 != l2) { + return l1 - l2; + } + ret = strncasecmp_m(name1, name2, l1); + if (ret != 0) { + return ret; + } + + /* the first parts are the same, check the suffix */ + if (c1 && c2) { + return strcasecmp_m(c1, c2); + } + + if (c1) { + return strcasecmp_m(c1, ":$DATA"); + } + if (c2) { + return strcasecmp_m(c2, ":$DATA"); + } + + /* neither names have a suffix */ + return 0; +} + /* return the list of file streams for RAW_FILEINFO_STREAM_INFORMATION @@ -66,9 +117,14 @@ NTSTATUS pvfs_stream_information(struct pvfs_state *pvfs, 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); + if (strchr(streams->streams[i].name, ':') == NULL) { + info->streams[i+1].stream_name.s = talloc_asprintf(streams->streams, + ":%s:$DATA", + streams->streams[i].name); + } else { + info->streams[i+1].stream_name.s = talloc_strdup(streams->streams, + streams->streams[i].name); + } } return NT_STATUS_OK; @@ -103,7 +159,7 @@ NTSTATUS pvfs_stream_info(struct pvfs_state *pvfs, struct pvfs_filename *name, i for (i=0;i<streams->num_streams;i++) { struct xattr_DosStream *s = &streams->streams[i]; - if (strcasecmp_m(s->name, name->stream_name) == 0) { + if (stream_name_cmp(s->name, name->stream_name) == 0) { name->dos.alloc_size = pvfs_round_alloc_size(pvfs, s->alloc_size); name->st.st_size = s->size; name->stream_exists = true; @@ -144,7 +200,7 @@ static NTSTATUS pvfs_stream_update_size(struct pvfs_state *pvfs, struct pvfs_fil for (i=0;i<streams->num_streams;i++) { struct xattr_DosStream *s = &streams->streams[i]; - if (strcasecmp_m(s->name, name->stream_name) == 0) { + if (stream_name_cmp(s->name, name->stream_name) == 0) { s->size = size; s->alloc_size = pvfs_round_alloc_size(pvfs, size); break; @@ -166,7 +222,79 @@ static NTSTATUS pvfs_stream_update_size(struct pvfs_state *pvfs, struct pvfs_fil s->flags = XATTR_STREAM_FLAG_INTERNAL; s->size = size; s->alloc_size = pvfs_round_alloc_size(pvfs, size); - s->name = name->stream_name; + s->name = stream_name_normalise(streams, name->stream_name); + if (s->name == NULL) { + talloc_free(streams); + return NT_STATUS_NO_MEMORY; + } + } + + status = pvfs_streams_save(pvfs, name, fd, streams); + talloc_free(streams); + + return status; +} + + +/* + rename a stream +*/ +NTSTATUS pvfs_stream_rename(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd, + const char *new_name) +{ + struct xattr_DosStreams *streams; + int i, found_old, found_new; + NTSTATUS status; + + streams = talloc(name, struct xattr_DosStreams); + if (streams == NULL) { + return NT_STATUS_NO_MEMORY; + } + + new_name = stream_name_normalise(streams, new_name); + if (new_name == NULL) { + return NT_STATUS_NO_MEMORY; + } + + status = pvfs_streams_load(pvfs, name, fd, streams); + if (!NT_STATUS_IS_OK(status)) { + ZERO_STRUCTP(streams); + } + + /* the default stream always exists */ + if (strcmp(new_name, "") == 0 || + strcasecmp_m(new_name, ":$DATA") == 0) { + return NT_STATUS_OBJECT_NAME_COLLISION; + } + + /* try to find the old/new names in the list */ + found_old = found_new = -1; + for (i=0;i<streams->num_streams;i++) { + struct xattr_DosStream *s = &streams->streams[i]; + if (stream_name_cmp(s->name, new_name) == 0) { + found_new = i; + } + if (stream_name_cmp(s->name, name->stream_name) == 0) { + found_old = i; + } + } + + if (found_old == -1) { + talloc_free(streams); + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + if (found_new == -1) { + /* a simple rename */ + struct xattr_DosStream *s = &streams->streams[found_old]; + s->name = new_name; + } else { + /* remove the old one and replace with the new one */ + streams->streams[found_old].name = new_name; + memmove(&streams->streams[found_new], + &streams->streams[found_new+1], + sizeof(streams->streams[0]) * + (streams->num_streams - (found_new+1))); } status = pvfs_streams_save(pvfs, name, fd, streams); @@ -222,7 +350,7 @@ NTSTATUS pvfs_stream_delete(struct pvfs_state *pvfs, for (i=0;i<streams->num_streams;i++) { struct xattr_DosStream *s = &streams->streams[i]; - if (strcasecmp_m(s->name, name->stream_name) == 0) { + if (stream_name_cmp(s->name, name->stream_name) == 0) { memmove(s, s+1, (streams->num_streams - (i+1)) * sizeof(*s)); streams->num_streams--; break; @@ -235,6 +363,54 @@ NTSTATUS pvfs_stream_delete(struct pvfs_state *pvfs, return status; } +/* + load a stream into a blob +*/ +static NTSTATUS pvfs_stream_load(struct pvfs_state *pvfs, + TALLOC_CTX *mem_ctx, + struct pvfs_filename *name, + int fd, + size_t estimated_size, + DATA_BLOB *blob) +{ + NTSTATUS status; + + status = pvfs_xattr_load(pvfs, mem_ctx, name->full_name, fd, + XATTR_DOSSTREAM_PREFIX, + name->stream_name, estimated_size, blob); + + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { + /* try with a case insensitive match */ + struct xattr_DosStreams *streams; + int i; + + streams = talloc(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)) { + talloc_free(streams); + return NT_STATUS_NOT_FOUND; + } + for (i=0;i<streams->num_streams;i++) { + struct xattr_DosStream *s = &streams->streams[i]; + if (stream_name_cmp(s->name, name->stream_name) == 0) { + status = pvfs_xattr_load(pvfs, mem_ctx, name->full_name, fd, + XATTR_DOSSTREAM_PREFIX, + s->name, estimated_size, blob); + talloc_free(streams); + return status; + } + } + talloc_free(streams); + return NT_STATUS_NOT_FOUND; + } + + return status; +} + /* the equvalent of pread() on a stream */ @@ -246,8 +422,7 @@ ssize_t pvfs_stream_read(struct pvfs_state *pvfs, 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); + status = pvfs_stream_load(pvfs, h, h->name, h->fd, offset+count, &blob); if (!NT_STATUS_IS_OK(status)) { errno = EIO; return -1; @@ -285,8 +460,7 @@ ssize_t pvfs_stream_write(struct pvfs_state *pvfs, } /* 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); + status = pvfs_stream_load(pvfs, h, h->name, h->fd, offset+count, &blob); if (!NT_STATUS_IS_OK(status)) { blob = data_blob(NULL, 0); } @@ -341,8 +515,7 @@ NTSTATUS pvfs_stream_truncate(struct pvfs_state *pvfs, } /* 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); + status = pvfs_stream_load(pvfs, name, name, fd, length, &blob); if (!NT_STATUS_IS_OK(status)) { return status; } |