2 Unix SMB/CIFS implementation.
4 POSIX NTVFS backend - alternate data streams
6 Copyright (C) Andrew Tridgell 2004
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "vfs_posix.h"
24 #include "librpc/gen_ndr/xattr.h"
27 normalise a stream name, removing a :$DATA suffix if there is one
28 Note: this returns the existing pointer to the name if the name does
31 static const char *stream_name_normalise(TALLOC_CTX
*ctx
, const char *name
)
33 const char *c
= strchr_m(name
, ':');
34 if (c
== NULL
|| strcasecmp_m(c
, ":$DATA") != 0) {
37 return talloc_strndup(ctx
, name
, c
-name
);
41 compare two stream names, taking account of the default $DATA extension
43 static int stream_name_cmp(const char *name1
, const char *name2
)
47 c1
= strchr_m(name1
, ':');
48 c2
= strchr_m(name2
, ':');
50 /* check the first part is the same */
51 l1
= c1
?(c1
- name1
):strlen(name1
);
52 l2
= c2
?(c2
- name2
):strlen(name2
);
56 ret
= strncasecmp_m(name1
, name2
, l1
);
61 /* the first parts are the same, check the suffix */
63 return strcasecmp_m(c1
, c2
);
67 return strcasecmp_m(c1
, ":$DATA");
70 return strcasecmp_m(c2
, ":$DATA");
73 /* neither names have a suffix */
79 return the list of file streams for RAW_FILEINFO_STREAM_INFORMATION
81 NTSTATUS
pvfs_stream_information(struct pvfs_state
*pvfs
,
83 struct pvfs_filename
*name
, int fd
,
84 struct stream_information
*info
)
86 struct xattr_DosStreams
*streams
;
90 /* directories don't have streams */
91 if (name
->dos
.attrib
& FILE_ATTRIBUTE_DIRECTORY
) {
92 info
->num_streams
= 0;
97 streams
= talloc(mem_ctx
, struct xattr_DosStreams
);
98 if (streams
== NULL
) {
99 return NT_STATUS_NO_MEMORY
;
102 status
= pvfs_streams_load(pvfs
, name
, fd
, streams
);
103 if (!NT_STATUS_IS_OK(status
)) {
104 ZERO_STRUCTP(streams
);
107 info
->num_streams
= streams
->num_streams
+1;
108 info
->streams
= talloc_array(mem_ctx
, struct stream_struct
, info
->num_streams
);
109 if (!info
->streams
) {
110 return NT_STATUS_NO_MEMORY
;
113 info
->streams
[0].size
= name
->st
.st_size
;
114 info
->streams
[0].alloc_size
= name
->dos
.alloc_size
;
115 info
->streams
[0].stream_name
.s
= talloc_strdup(info
->streams
, "::$DATA");
117 for (i
=0;i
<streams
->num_streams
;i
++) {
118 info
->streams
[i
+1].size
= streams
->streams
[i
].size
;
119 info
->streams
[i
+1].alloc_size
= streams
->streams
[i
].alloc_size
;
120 if (strchr(streams
->streams
[i
].name
, ':') == NULL
) {
121 info
->streams
[i
+1].stream_name
.s
= talloc_asprintf(streams
->streams
,
123 streams
->streams
[i
].name
);
125 info
->streams
[i
+1].stream_name
.s
= talloc_strdup(streams
->streams
,
126 streams
->streams
[i
].name
);
135 fill in the stream information for a name
137 NTSTATUS
pvfs_stream_info(struct pvfs_state
*pvfs
, struct pvfs_filename
*name
, int fd
)
139 struct xattr_DosStreams
*streams
;
143 /* the NULL stream always exists */
144 if (name
->stream_name
== NULL
) {
145 name
->stream_exists
= true;
149 streams
= talloc(name
, struct xattr_DosStreams
);
150 if (streams
== NULL
) {
151 return NT_STATUS_NO_MEMORY
;
154 status
= pvfs_streams_load(pvfs
, name
, fd
, streams
);
155 if (!NT_STATUS_IS_OK(status
)) {
156 talloc_free(streams
);
160 for (i
=0;i
<streams
->num_streams
;i
++) {
161 struct xattr_DosStream
*s
= &streams
->streams
[i
];
162 if (stream_name_cmp(s
->name
, name
->stream_name
) == 0) {
163 name
->dos
.alloc_size
= pvfs_round_alloc_size(pvfs
, s
->alloc_size
);
164 name
->st
.st_size
= s
->size
;
165 name
->stream_exists
= true;
166 talloc_free(streams
);
171 talloc_free(streams
);
173 name
->dos
.alloc_size
= 0;
174 name
->st
.st_size
= 0;
175 name
->stream_exists
= false;
182 update size information for a stream
184 static NTSTATUS
pvfs_stream_update_size(struct pvfs_state
*pvfs
, struct pvfs_filename
*name
, int fd
,
187 struct xattr_DosStreams
*streams
;
191 streams
= talloc(name
, struct xattr_DosStreams
);
192 if (streams
== NULL
) {
193 return NT_STATUS_NO_MEMORY
;
196 status
= pvfs_streams_load(pvfs
, name
, fd
, streams
);
197 if (!NT_STATUS_IS_OK(status
)) {
198 ZERO_STRUCTP(streams
);
201 for (i
=0;i
<streams
->num_streams
;i
++) {
202 struct xattr_DosStream
*s
= &streams
->streams
[i
];
203 if (stream_name_cmp(s
->name
, name
->stream_name
) == 0) {
205 s
->alloc_size
= pvfs_round_alloc_size(pvfs
, size
);
210 if (i
== streams
->num_streams
) {
211 struct xattr_DosStream
*s
;
212 streams
->streams
= talloc_realloc(streams
, streams
->streams
,
213 struct xattr_DosStream
,
214 streams
->num_streams
+1);
215 if (streams
->streams
== NULL
) {
216 talloc_free(streams
);
217 return NT_STATUS_NO_MEMORY
;
219 streams
->num_streams
++;
220 s
= &streams
->streams
[i
];
222 s
->flags
= XATTR_STREAM_FLAG_INTERNAL
;
224 s
->alloc_size
= pvfs_round_alloc_size(pvfs
, size
);
225 s
->name
= stream_name_normalise(streams
, name
->stream_name
);
226 if (s
->name
== NULL
) {
227 talloc_free(streams
);
228 return NT_STATUS_NO_MEMORY
;
232 status
= pvfs_streams_save(pvfs
, name
, fd
, streams
);
233 talloc_free(streams
);
242 NTSTATUS
pvfs_stream_rename(struct pvfs_state
*pvfs
, struct pvfs_filename
*name
, int fd
,
243 const char *new_name
, bool overwrite
)
245 struct xattr_DosStreams
*streams
;
246 int i
, found_old
, found_new
;
249 streams
= talloc(name
, struct xattr_DosStreams
);
250 if (streams
== NULL
) {
251 return NT_STATUS_NO_MEMORY
;
254 new_name
= stream_name_normalise(streams
, new_name
);
255 if (new_name
== NULL
) {
256 return NT_STATUS_NO_MEMORY
;
259 status
= pvfs_streams_load(pvfs
, name
, fd
, streams
);
260 if (!NT_STATUS_IS_OK(status
)) {
261 ZERO_STRUCTP(streams
);
264 /* the default stream always exists */
265 if (strcmp(new_name
, "") == 0 ||
266 strcasecmp_m(new_name
, ":$DATA") == 0) {
267 return NT_STATUS_OBJECT_NAME_COLLISION
;
270 /* try to find the old/new names in the list */
271 found_old
= found_new
= -1;
272 for (i
=0;i
<streams
->num_streams
;i
++) {
273 struct xattr_DosStream
*s
= &streams
->streams
[i
];
274 if (stream_name_cmp(s
->name
, new_name
) == 0) {
277 if (stream_name_cmp(s
->name
, name
->stream_name
) == 0) {
282 if (found_old
== -1) {
283 talloc_free(streams
);
284 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
287 if (found_new
== -1) {
288 /* a simple rename */
289 struct xattr_DosStream
*s
= &streams
->streams
[found_old
];
293 return NT_STATUS_OBJECT_NAME_COLLISION
;
295 if (found_old
!= found_new
) {
296 /* remove the old one and replace with the new one */
297 streams
->streams
[found_old
].name
= new_name
;
298 memmove(&streams
->streams
[found_new
],
299 &streams
->streams
[found_new
+1],
300 sizeof(streams
->streams
[0]) *
301 (streams
->num_streams
- (found_new
+1)));
302 streams
->num_streams
--;
306 status
= pvfs_streams_save(pvfs
, name
, fd
, streams
);
308 if (NT_STATUS_IS_OK(status
)) {
310 /* update the in-memory copy of the name of the open file */
311 talloc_free(name
->stream_name
);
312 name
->stream_name
= talloc_strdup(name
, new_name
);
314 talloc_free(streams
);
322 create the xattr for a alternate data stream
324 NTSTATUS
pvfs_stream_create(struct pvfs_state
*pvfs
,
325 struct pvfs_filename
*name
,
329 status
= pvfs_xattr_create(pvfs
, name
->full_name
, fd
,
330 XATTR_DOSSTREAM_PREFIX
, name
->stream_name
);
331 if (!NT_STATUS_IS_OK(status
)) {
334 return pvfs_stream_update_size(pvfs
, name
, fd
, 0);
338 delete the xattr for a alternate data stream
340 NTSTATUS
pvfs_stream_delete(struct pvfs_state
*pvfs
,
341 struct pvfs_filename
*name
,
345 struct xattr_DosStreams
*streams
;
348 status
= pvfs_xattr_delete(pvfs
, name
->full_name
, fd
,
349 XATTR_DOSSTREAM_PREFIX
, name
->stream_name
);
350 if (!NT_STATUS_IS_OK(status
)) {
354 streams
= talloc(name
, struct xattr_DosStreams
);
355 if (streams
== NULL
) {
356 return NT_STATUS_NO_MEMORY
;
359 status
= pvfs_streams_load(pvfs
, name
, fd
, streams
);
360 if (!NT_STATUS_IS_OK(status
)) {
361 talloc_free(streams
);
365 for (i
=0;i
<streams
->num_streams
;i
++) {
366 struct xattr_DosStream
*s
= &streams
->streams
[i
];
367 if (stream_name_cmp(s
->name
, name
->stream_name
) == 0) {
368 memmove(s
, s
+1, (streams
->num_streams
- (i
+1)) * sizeof(*s
));
369 streams
->num_streams
--;
374 status
= pvfs_streams_save(pvfs
, name
, fd
, streams
);
375 talloc_free(streams
);
381 load a stream into a blob
383 static NTSTATUS
pvfs_stream_load(struct pvfs_state
*pvfs
,
385 struct pvfs_filename
*name
,
387 size_t estimated_size
,
392 status
= pvfs_xattr_load(pvfs
, mem_ctx
, name
->full_name
, fd
,
393 XATTR_DOSSTREAM_PREFIX
,
394 name
->stream_name
, estimated_size
, blob
);
396 if (NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
397 /* try with a case insensitive match */
398 struct xattr_DosStreams
*streams
;
401 streams
= talloc(mem_ctx
, struct xattr_DosStreams
);
402 if (streams
== NULL
) {
403 return NT_STATUS_NO_MEMORY
;
406 status
= pvfs_streams_load(pvfs
, name
, fd
, streams
);
407 if (!NT_STATUS_IS_OK(status
)) {
408 talloc_free(streams
);
409 return NT_STATUS_NOT_FOUND
;
411 for (i
=0;i
<streams
->num_streams
;i
++) {
412 struct xattr_DosStream
*s
= &streams
->streams
[i
];
413 if (stream_name_cmp(s
->name
, name
->stream_name
) == 0) {
414 status
= pvfs_xattr_load(pvfs
, mem_ctx
, name
->full_name
, fd
,
415 XATTR_DOSSTREAM_PREFIX
,
416 s
->name
, estimated_size
, blob
);
417 talloc_free(streams
);
421 talloc_free(streams
);
422 return NT_STATUS_NOT_FOUND
;
429 the equvalent of pread() on a stream
431 ssize_t
pvfs_stream_read(struct pvfs_state
*pvfs
,
432 struct pvfs_file_handle
*h
, void *data
, size_t count
, off_t offset
)
439 status
= pvfs_stream_load(pvfs
, h
, h
->name
, h
->fd
, offset
+count
, &blob
);
440 if (!NT_STATUS_IS_OK(status
)) {
444 if (offset
>= blob
.length
) {
445 data_blob_free(&blob
);
448 if (count
> blob
.length
- offset
) {
449 count
= blob
.length
- offset
;
451 memcpy(data
, blob
.data
+ offset
, count
);
452 data_blob_free(&blob
);
458 the equvalent of pwrite() on a stream
460 ssize_t
pvfs_stream_write(struct pvfs_state
*pvfs
,
461 struct pvfs_file_handle
*h
, const void *data
, size_t count
, off_t offset
)
469 if (count
+offset
> XATTR_MAX_STREAM_SIZE
) {
470 if (!pvfs
->ea_db
|| count
+offset
> XATTR_MAX_STREAM_SIZE_TDB
) {
476 /* we have to load the existing stream, then modify, then save */
477 status
= pvfs_stream_load(pvfs
, h
, h
->name
, h
->fd
, offset
+count
, &blob
);
478 if (!NT_STATUS_IS_OK(status
)) {
479 blob
= data_blob(NULL
, 0);
481 if (count
+offset
> blob
.length
) {
482 blob
.data
= talloc_realloc(blob
.data
, blob
.data
, uint8_t, count
+offset
);
483 if (blob
.data
== NULL
) {
487 if (offset
> blob
.length
) {
488 memset(blob
.data
+blob
.length
, 0, offset
- blob
.length
);
490 blob
.length
= count
+offset
;
492 memcpy(blob
.data
+ offset
, data
, count
);
494 status
= pvfs_xattr_save(pvfs
, h
->name
->full_name
, h
->fd
, XATTR_DOSSTREAM_PREFIX
,
495 h
->name
->stream_name
, &blob
);
496 if (!NT_STATUS_IS_OK(status
)) {
497 data_blob_free(&blob
);
498 /* getting this error mapping right is probably
504 status
= pvfs_stream_update_size(pvfs
, h
->name
, h
->fd
, blob
.length
);
506 data_blob_free(&blob
);
508 if (!NT_STATUS_IS_OK(status
)) {
517 the equvalent of truncate() on a stream
519 NTSTATUS
pvfs_stream_truncate(struct pvfs_state
*pvfs
,
520 struct pvfs_filename
*name
, int fd
, off_t length
)
525 if (length
> XATTR_MAX_STREAM_SIZE
) {
526 if (!pvfs
->ea_db
|| length
> XATTR_MAX_STREAM_SIZE_TDB
) {
527 return NT_STATUS_DISK_FULL
;
531 /* we have to load the existing stream, then modify, then save */
532 status
= pvfs_stream_load(pvfs
, name
, name
, fd
, length
, &blob
);
533 if (!NT_STATUS_IS_OK(status
)) {
536 if (length
<= blob
.length
) {
537 blob
.length
= length
;
538 } else if (length
> blob
.length
) {
539 blob
.data
= talloc_realloc(blob
.data
, blob
.data
, uint8_t, length
);
540 if (blob
.data
== NULL
) {
541 return NT_STATUS_NO_MEMORY
;
543 memset(blob
.data
+blob
.length
, 0, length
- blob
.length
);
544 blob
.length
= length
;
547 status
= pvfs_xattr_save(pvfs
, name
->full_name
, fd
, XATTR_DOSSTREAM_PREFIX
,
548 name
->stream_name
, &blob
);
550 if (NT_STATUS_IS_OK(status
)) {
551 status
= pvfs_stream_update_size(pvfs
, name
, fd
, blob
.length
);
553 data_blob_free(&blob
);