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