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