Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : POSIX NTVFS backend - alternate data streams
5 :
6 : Copyright (C) Andrew Tridgell 2004
7 :
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.
12 :
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.
17 :
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/>.
20 : */
21 :
22 : #include "includes.h"
23 : #include "vfs_posix.h"
24 : #include "librpc/gen_ndr/xattr.h"
25 : #include "lib/util/tsort.h"
26 :
27 : /*
28 : normalise a stream name, removing a :$DATA suffix if there is one
29 : Note: this returns the existing pointer to the name if the name does
30 : not need normalising
31 : */
32 61 : static const char *stream_name_normalise(TALLOC_CTX *ctx, const char *name)
33 : {
34 61 : const char *c = strchr_m(name, ':');
35 61 : if (c == NULL || strcasecmp_m(c, ":$DATA") != 0) {
36 51 : return name;
37 : }
38 10 : return talloc_strndup(ctx, name, c-name);
39 : }
40 :
41 : /*
42 : compare two stream names, taking account of the default $DATA extension
43 : */
44 452 : static int stream_name_cmp(const char *name1, const char *name2)
45 : {
46 0 : const char *c1, *c2;
47 0 : int l1, l2, ret;
48 452 : c1 = strchr_m(name1, ':');
49 452 : c2 = strchr_m(name2, ':');
50 :
51 : /* check the first part is the same */
52 452 : l1 = c1?(c1 - name1):strlen(name1);
53 452 : l2 = c2?(c2 - name2):strlen(name2);
54 452 : if (l1 != l2) {
55 136 : return NUMERIC_CMP(l1, l2);
56 : }
57 316 : ret = strncasecmp_m(name1, name2, l1);
58 316 : if (ret != 0) {
59 24 : return ret;
60 : }
61 :
62 : /* the first parts are the same, check the suffix */
63 292 : if (c1 && c2) {
64 0 : return strcasecmp_m(c1, c2);
65 : }
66 :
67 292 : if (c1) {
68 0 : return strcasecmp_m(c1, ":$DATA");
69 : }
70 292 : if (c2) {
71 0 : return strcasecmp_m(c2, ":$DATA");
72 : }
73 :
74 : /* neither names have a suffix */
75 292 : return 0;
76 : }
77 :
78 :
79 : /*
80 : return the list of file streams for RAW_FILEINFO_STREAM_INFORMATION
81 : */
82 42 : NTSTATUS pvfs_stream_information(struct pvfs_state *pvfs,
83 : TALLOC_CTX *mem_ctx,
84 : struct pvfs_filename *name, int fd,
85 : struct stream_information *info)
86 : {
87 0 : struct xattr_DosStreams *streams;
88 0 : int i;
89 0 : NTSTATUS status;
90 :
91 : /* directories don't have streams */
92 42 : if (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
93 2 : info->num_streams = 0;
94 2 : info->streams = NULL;
95 2 : return NT_STATUS_OK;
96 : }
97 :
98 40 : streams = talloc(mem_ctx, struct xattr_DosStreams);
99 40 : if (streams == NULL) {
100 0 : return NT_STATUS_NO_MEMORY;
101 : }
102 :
103 40 : status = pvfs_streams_load(pvfs, name, fd, streams);
104 40 : if (!NT_STATUS_IS_OK(status)) {
105 0 : ZERO_STRUCTP(streams);
106 : }
107 :
108 40 : info->num_streams = streams->num_streams+1;
109 40 : info->streams = talloc_array(mem_ctx, struct stream_struct, info->num_streams);
110 40 : if (!info->streams) {
111 0 : return NT_STATUS_NO_MEMORY;
112 : }
113 :
114 40 : info->streams[0].size = name->st.st_size;
115 40 : info->streams[0].alloc_size = name->dos.alloc_size;
116 40 : info->streams[0].stream_name.s = talloc_strdup(info->streams, "::$DATA");
117 :
118 88 : for (i=0;i<streams->num_streams;i++) {
119 48 : info->streams[i+1].size = streams->streams[i].size;
120 48 : info->streams[i+1].alloc_size = streams->streams[i].alloc_size;
121 48 : if (strchr(streams->streams[i].name, ':') == NULL) {
122 48 : info->streams[i+1].stream_name.s = talloc_asprintf(streams->streams,
123 : ":%s:$DATA",
124 48 : streams->streams[i].name);
125 : } else {
126 0 : info->streams[i+1].stream_name.s = talloc_strdup(streams->streams,
127 0 : streams->streams[i].name);
128 : }
129 : }
130 :
131 40 : return NT_STATUS_OK;
132 : }
133 :
134 :
135 : /*
136 : fill in the stream information for a name
137 : */
138 806531 : NTSTATUS pvfs_stream_info(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd)
139 : {
140 0 : struct xattr_DosStreams *streams;
141 0 : int i;
142 0 : NTSTATUS status;
143 :
144 : /* the NULL stream always exists */
145 806531 : if (name->stream_name == NULL) {
146 806002 : name->stream_exists = true;
147 806002 : return NT_STATUS_OK;
148 : }
149 :
150 529 : streams = talloc(name, struct xattr_DosStreams);
151 529 : if (streams == NULL) {
152 0 : return NT_STATUS_NO_MEMORY;
153 : }
154 :
155 529 : status = pvfs_streams_load(pvfs, name, fd, streams);
156 529 : if (!NT_STATUS_IS_OK(status)) {
157 0 : talloc_free(streams);
158 0 : return status;
159 : }
160 :
161 627 : for (i=0;i<streams->num_streams;i++) {
162 342 : struct xattr_DosStream *s = &streams->streams[i];
163 342 : if (stream_name_cmp(s->name, name->stream_name) == 0) {
164 244 : name->dos.alloc_size = pvfs_round_alloc_size(pvfs, s->alloc_size);
165 244 : name->st.st_size = s->size;
166 244 : name->stream_exists = true;
167 244 : talloc_free(streams);
168 244 : return NT_STATUS_OK;
169 : }
170 : }
171 :
172 285 : talloc_free(streams);
173 :
174 285 : name->dos.alloc_size = 0;
175 285 : name->st.st_size = 0;
176 285 : name->stream_exists = false;
177 :
178 285 : return NT_STATUS_OK;
179 : }
180 :
181 :
182 : /*
183 : update size information for a stream
184 : */
185 69 : static NTSTATUS pvfs_stream_update_size(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
186 : off_t size)
187 : {
188 0 : struct xattr_DosStreams *streams;
189 0 : int i;
190 0 : NTSTATUS status;
191 :
192 69 : streams = talloc(name, struct xattr_DosStreams);
193 69 : if (streams == NULL) {
194 0 : return NT_STATUS_NO_MEMORY;
195 : }
196 :
197 69 : status = pvfs_streams_load(pvfs, name, fd, streams);
198 69 : if (!NT_STATUS_IS_OK(status)) {
199 0 : ZERO_STRUCTP(streams);
200 : }
201 :
202 98 : for (i=0;i<streams->num_streams;i++) {
203 49 : struct xattr_DosStream *s = &streams->streams[i];
204 49 : if (stream_name_cmp(s->name, name->stream_name) == 0) {
205 20 : s->size = size;
206 20 : s->alloc_size = pvfs_round_alloc_size(pvfs, size);
207 20 : break;
208 : }
209 : }
210 :
211 69 : if (i == streams->num_streams) {
212 0 : struct xattr_DosStream *s;
213 49 : streams->streams = talloc_realloc(streams, streams->streams,
214 : struct xattr_DosStream,
215 : streams->num_streams+1);
216 49 : if (streams->streams == NULL) {
217 0 : talloc_free(streams);
218 0 : return NT_STATUS_NO_MEMORY;
219 : }
220 49 : streams->num_streams++;
221 49 : s = &streams->streams[i];
222 :
223 49 : s->flags = XATTR_STREAM_FLAG_INTERNAL;
224 49 : s->size = size;
225 49 : s->alloc_size = pvfs_round_alloc_size(pvfs, size);
226 49 : s->name = stream_name_normalise(streams, name->stream_name);
227 49 : if (s->name == NULL) {
228 0 : talloc_free(streams);
229 0 : return NT_STATUS_NO_MEMORY;
230 : }
231 : }
232 :
233 69 : status = pvfs_streams_save(pvfs, name, fd, streams);
234 69 : talloc_free(streams);
235 :
236 69 : return status;
237 : }
238 :
239 :
240 : /*
241 : rename a stream
242 : */
243 12 : NTSTATUS pvfs_stream_rename(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
244 : const char *new_name, bool overwrite)
245 : {
246 0 : struct xattr_DosStreams *streams;
247 0 : int i, found_old, found_new;
248 0 : NTSTATUS status;
249 :
250 12 : streams = talloc(name, struct xattr_DosStreams);
251 12 : if (streams == NULL) {
252 0 : return NT_STATUS_NO_MEMORY;
253 : }
254 :
255 12 : new_name = stream_name_normalise(streams, new_name);
256 12 : if (new_name == NULL) {
257 0 : return NT_STATUS_NO_MEMORY;
258 : }
259 :
260 12 : status = pvfs_streams_load(pvfs, name, fd, streams);
261 12 : if (!NT_STATUS_IS_OK(status)) {
262 0 : ZERO_STRUCTP(streams);
263 : }
264 :
265 : /* the default stream always exists */
266 23 : if (strcmp(new_name, "") == 0 ||
267 11 : strcasecmp_m(new_name, ":$DATA") == 0) {
268 1 : return NT_STATUS_OBJECT_NAME_COLLISION;
269 : }
270 :
271 : /* try to find the old/new names in the list */
272 11 : found_old = found_new = -1;
273 36 : for (i=0;i<streams->num_streams;i++) {
274 25 : struct xattr_DosStream *s = &streams->streams[i];
275 25 : if (stream_name_cmp(s->name, new_name) == 0) {
276 8 : found_new = i;
277 : }
278 25 : if (stream_name_cmp(s->name, name->stream_name) == 0) {
279 11 : found_old = i;
280 : }
281 : }
282 :
283 11 : if (found_old == -1) {
284 0 : talloc_free(streams);
285 0 : return NT_STATUS_OBJECT_NAME_NOT_FOUND;
286 : }
287 :
288 11 : if (found_new == -1) {
289 : /* a simple rename */
290 3 : struct xattr_DosStream *s = &streams->streams[found_old];
291 3 : s->name = new_name;
292 : } else {
293 8 : if (!overwrite) {
294 1 : return NT_STATUS_OBJECT_NAME_COLLISION;
295 : }
296 7 : if (found_old != found_new) {
297 : /* remove the old one and replace with the new one */
298 6 : streams->streams[found_old].name = new_name;
299 6 : memmove(&streams->streams[found_new],
300 6 : &streams->streams[found_new+1],
301 : sizeof(streams->streams[0]) *
302 6 : (streams->num_streams - (found_new+1)));
303 6 : streams->num_streams--;
304 : }
305 : }
306 :
307 10 : status = pvfs_streams_save(pvfs, name, fd, streams);
308 :
309 10 : if (NT_STATUS_IS_OK(status)) {
310 :
311 : /* update the in-memory copy of the name of the open file */
312 10 : talloc_free(name->stream_name);
313 10 : name->stream_name = talloc_strdup(name, new_name);
314 :
315 10 : talloc_free(streams);
316 : }
317 :
318 10 : return status;
319 : }
320 :
321 :
322 : /*
323 : create the xattr for a alternate data stream
324 : */
325 49 : NTSTATUS pvfs_stream_create(struct pvfs_state *pvfs,
326 : struct pvfs_filename *name,
327 : int fd)
328 : {
329 0 : NTSTATUS status;
330 49 : status = pvfs_xattr_create(pvfs, name->full_name, fd,
331 49 : XATTR_DOSSTREAM_PREFIX, name->stream_name);
332 49 : if (!NT_STATUS_IS_OK(status)) {
333 0 : return status;
334 : }
335 49 : return pvfs_stream_update_size(pvfs, name, fd, 0);
336 : }
337 :
338 : /*
339 : delete the xattr for a alternate data stream
340 : */
341 7 : NTSTATUS pvfs_stream_delete(struct pvfs_state *pvfs,
342 : struct pvfs_filename *name,
343 : int fd)
344 : {
345 0 : NTSTATUS status;
346 0 : struct xattr_DosStreams *streams;
347 0 : int i;
348 :
349 7 : status = pvfs_xattr_delete(pvfs, name->full_name, fd,
350 7 : XATTR_DOSSTREAM_PREFIX, name->stream_name);
351 7 : if (!NT_STATUS_IS_OK(status)) {
352 0 : return status;
353 : }
354 :
355 7 : streams = talloc(name, struct xattr_DosStreams);
356 7 : if (streams == NULL) {
357 0 : return NT_STATUS_NO_MEMORY;
358 : }
359 :
360 7 : status = pvfs_streams_load(pvfs, name, fd, streams);
361 7 : if (!NT_STATUS_IS_OK(status)) {
362 0 : talloc_free(streams);
363 0 : return status;
364 : }
365 :
366 7 : for (i=0;i<streams->num_streams;i++) {
367 7 : struct xattr_DosStream *s = &streams->streams[i];
368 7 : if (stream_name_cmp(s->name, name->stream_name) == 0) {
369 7 : memmove(s, s+1, (streams->num_streams - (i+1)) * sizeof(*s));
370 7 : streams->num_streams--;
371 7 : break;
372 : }
373 : }
374 :
375 7 : status = pvfs_streams_save(pvfs, name, fd, streams);
376 7 : talloc_free(streams);
377 :
378 7 : return status;
379 : }
380 :
381 : /*
382 : load a stream into a blob
383 : */
384 38 : static NTSTATUS pvfs_stream_load(struct pvfs_state *pvfs,
385 : TALLOC_CTX *mem_ctx,
386 : struct pvfs_filename *name,
387 : int fd,
388 : size_t estimated_size,
389 : DATA_BLOB *blob)
390 : {
391 0 : NTSTATUS status;
392 :
393 38 : status = pvfs_xattr_load(pvfs, mem_ctx, name->full_name, fd,
394 : XATTR_DOSSTREAM_PREFIX,
395 38 : name->stream_name, estimated_size, blob);
396 :
397 38 : if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
398 : /* try with a case insensitive match */
399 0 : struct xattr_DosStreams *streams;
400 0 : int i;
401 :
402 2 : streams = talloc(mem_ctx, struct xattr_DosStreams);
403 2 : if (streams == NULL) {
404 0 : return NT_STATUS_NO_MEMORY;
405 : }
406 :
407 2 : status = pvfs_streams_load(pvfs, name, fd, streams);
408 2 : if (!NT_STATUS_IS_OK(status)) {
409 0 : talloc_free(streams);
410 0 : return NT_STATUS_NOT_FOUND;
411 : }
412 4 : for (i=0;i<streams->num_streams;i++) {
413 4 : struct xattr_DosStream *s = &streams->streams[i];
414 4 : if (stream_name_cmp(s->name, name->stream_name) == 0) {
415 2 : status = pvfs_xattr_load(pvfs, mem_ctx, name->full_name, fd,
416 : XATTR_DOSSTREAM_PREFIX,
417 : s->name, estimated_size, blob);
418 2 : talloc_free(streams);
419 2 : return status;
420 : }
421 : }
422 0 : talloc_free(streams);
423 0 : return NT_STATUS_NOT_FOUND;
424 : }
425 :
426 36 : return status;
427 : }
428 :
429 : /*
430 : the equivalent of pread() on a stream
431 : */
432 18 : ssize_t pvfs_stream_read(struct pvfs_state *pvfs,
433 : struct pvfs_file_handle *h, void *data, size_t count, off_t offset)
434 : {
435 0 : NTSTATUS status;
436 0 : DATA_BLOB blob;
437 18 : if (count == 0) {
438 0 : return 0;
439 : }
440 18 : status = pvfs_stream_load(pvfs, h, h->name, h->fd, offset+count, &blob);
441 18 : if (!NT_STATUS_IS_OK(status)) {
442 0 : errno = EIO;
443 0 : return -1;
444 : }
445 18 : if (offset >= blob.length) {
446 6 : data_blob_free(&blob);
447 6 : return 0;
448 : }
449 12 : if (count > blob.length - offset) {
450 12 : count = blob.length - offset;
451 : }
452 12 : memcpy(data, blob.data + offset, count);
453 12 : data_blob_free(&blob);
454 12 : return count;
455 : }
456 :
457 :
458 : /*
459 : the equivalent of pwrite() on a stream
460 : */
461 16 : ssize_t pvfs_stream_write(struct pvfs_state *pvfs,
462 : struct pvfs_file_handle *h, const void *data, size_t count, off_t offset)
463 : {
464 0 : NTSTATUS status;
465 0 : DATA_BLOB blob;
466 16 : if (count == 0) {
467 2 : return 0;
468 : }
469 :
470 14 : if (count+offset > XATTR_MAX_STREAM_SIZE) {
471 2 : if (!pvfs->ea_db || count+offset > XATTR_MAX_STREAM_SIZE_TDB) {
472 0 : errno = ENOSPC;
473 0 : return -1;
474 : }
475 : }
476 :
477 : /* we have to load the existing stream, then modify, then save */
478 14 : status = pvfs_stream_load(pvfs, h, h->name, h->fd, offset+count, &blob);
479 14 : if (!NT_STATUS_IS_OK(status)) {
480 0 : blob = data_blob(NULL, 0);
481 : }
482 14 : if (count+offset > blob.length) {
483 14 : blob.data = talloc_realloc(blob.data, blob.data, uint8_t, count+offset);
484 14 : if (blob.data == NULL) {
485 0 : errno = ENOMEM;
486 0 : return -1;
487 : }
488 14 : if (offset > blob.length) {
489 2 : memset(blob.data+blob.length, 0, offset - blob.length);
490 : }
491 14 : blob.length = count+offset;
492 : }
493 14 : memcpy(blob.data + offset, data, count);
494 :
495 14 : status = pvfs_xattr_save(pvfs, h->name->full_name, h->fd, XATTR_DOSSTREAM_PREFIX,
496 14 : h->name->stream_name, &blob);
497 14 : if (!NT_STATUS_IS_OK(status)) {
498 0 : data_blob_free(&blob);
499 : /* getting this error mapping right is probably
500 : not worth it */
501 0 : errno = ENOSPC;
502 0 : return -1;
503 : }
504 :
505 14 : status = pvfs_stream_update_size(pvfs, h->name, h->fd, blob.length);
506 :
507 14 : data_blob_free(&blob);
508 :
509 14 : if (!NT_STATUS_IS_OK(status)) {
510 0 : errno = EIO;
511 0 : return -1;
512 : }
513 :
514 14 : return count;
515 : }
516 :
517 : /*
518 : the equivalent of truncate() on a stream
519 : */
520 6 : NTSTATUS pvfs_stream_truncate(struct pvfs_state *pvfs,
521 : struct pvfs_filename *name, int fd, off_t length)
522 : {
523 0 : NTSTATUS status;
524 0 : DATA_BLOB blob;
525 :
526 6 : if (length > XATTR_MAX_STREAM_SIZE) {
527 1 : if (!pvfs->ea_db || length > XATTR_MAX_STREAM_SIZE_TDB) {
528 0 : return NT_STATUS_DISK_FULL;
529 : }
530 : }
531 :
532 : /* we have to load the existing stream, then modify, then save */
533 6 : status = pvfs_stream_load(pvfs, name, name, fd, length, &blob);
534 6 : if (!NT_STATUS_IS_OK(status)) {
535 0 : return status;
536 : }
537 6 : if (length <= blob.length) {
538 3 : blob.length = length;
539 3 : } else if (length > blob.length) {
540 3 : blob.data = talloc_realloc(blob.data, blob.data, uint8_t, length);
541 3 : if (blob.data == NULL) {
542 0 : return NT_STATUS_NO_MEMORY;
543 : }
544 3 : memset(blob.data+blob.length, 0, length - blob.length);
545 3 : blob.length = length;
546 : }
547 :
548 6 : status = pvfs_xattr_save(pvfs, name->full_name, fd, XATTR_DOSSTREAM_PREFIX,
549 6 : name->stream_name, &blob);
550 :
551 6 : if (NT_STATUS_IS_OK(status)) {
552 6 : status = pvfs_stream_update_size(pvfs, name, fd, blob.length);
553 : }
554 6 : data_blob_free(&blob);
555 :
556 6 : return status;
557 : }
|