1/* 2 Copyright 1999-2001, Be Incorporated. All Rights Reserved. 3 This file may be used under the terms of the Be Sample Code License. 4*/ 5/* attributes.c 6 * handles mime type information for ntfs 7 * gets/sets mime information in vnode 8 */ 9 10 11#include <KernelExport.h> 12#include <SupportDefs.h> 13#include <TypeConstants.h> 14 15#include <dirent.h> 16#include <fs_attr.h> 17#include <stdlib.h> 18#include <string.h> 19 20#include "attributes.h" 21#include "mime_table.h" 22#include "ntfs.h" 23 24 25const char* kHaikuAttrPrefix={"HAIKU-XATTR:"}; 26 27 28status_t 29fs_open_attrib_dir(fs_volume *_vol, fs_vnode *_node, void **_cookie) 30{ 31 nspace *ns = (nspace*)_vol->private_volume; 32 vnode *node = (vnode*)_node->private_node; 33 attrdircookie *cookie = NULL; 34 ntfs_inode *ni = NULL; 35 ntfs_attr_search_ctx *ctx = NULL; 36 37 status_t result = B_NO_ERROR; 38 39 TRACE("%s - ENTER\n", __FUNCTION__); 40 41 LOCK_VOL(ns); 42 43 ni = ntfs_inode_open(ns->ntvol, node->vnid); 44 if (ni == NULL) { 45 result = errno; 46 goto exit; 47 } 48 49 ctx = ntfs_attr_get_search_ctx(ni, NULL); 50 if (ctx == NULL) { 51 result = errno; 52 goto exit; 53 } 54 55 cookie = (attrdircookie*)ntfs_calloc(sizeof(attrdircookie)); 56 if (cookie == NULL) { 57 result = ENOMEM; 58 goto exit; 59 } 60 61 cookie->inode = ni; 62 cookie->ctx = ctx; 63 ni = NULL; 64 ctx = NULL; 65 *_cookie = cookie; 66 67exit: 68 69 if (ctx != NULL) 70 ntfs_attr_put_search_ctx(ctx); 71 if (ni != NULL) 72 ntfs_inode_close(ni); 73 74 TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result)); 75 76 UNLOCK_VOL(ns); 77 78 return result; 79} 80 81 82status_t 83fs_close_attrib_dir(fs_volume *_vol, fs_vnode *_node, void *_cookie) 84{ 85 return B_NO_ERROR; 86} 87 88 89status_t 90fs_free_attrib_dir_cookie(fs_volume *_vol, fs_vnode *_node, void *_cookie) 91{ 92 nspace *ns = (nspace *)_vol->private_volume; 93 attrdircookie *cookie = (attrdircookie *)_cookie; 94 95 LOCK_VOL(ns); 96 97 if (cookie->ctx) 98 ntfs_attr_put_search_ctx(cookie->ctx); 99 if (cookie->inode) 100 ntfs_inode_close(cookie->inode); 101 102 UNLOCK_VOL(ns); 103 104 free(cookie); 105 return B_NO_ERROR; 106} 107 108 109status_t 110fs_rewind_attrib_dir(fs_volume *_vol, fs_vnode *_node, void *_cookie) 111{ 112 nspace *ns = (nspace*)_vol->private_volume; 113 attrdircookie *cookie = (attrdircookie *)_cookie; 114 status_t result = B_NO_ERROR; 115 116 TRACE("%s - ENTER\n", __FUNCTION__); 117 118 LOCK_VOL(ns); 119 120 if (cookie->ctx) 121 ntfs_attr_put_search_ctx(cookie->ctx); 122 cookie->ctx = ntfs_attr_get_search_ctx(cookie->inode, NULL); 123 if (cookie->ctx == NULL) { 124 result = errno; 125 //goto exit; 126 } 127 128//exit: 129 130 TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result)); 131 132 UNLOCK_VOL(ns); 133 134 return result; 135} 136 137 138status_t 139fs_read_attrib_dir(fs_volume *_vol, fs_vnode *_node, void *_cookie, 140 struct dirent *entry, size_t bufsize, uint32 *num) 141{ 142 nspace *ns = (nspace *)_vol->private_volume; 143 vnode *node = (vnode *)_node->private_node; 144 char *name = NULL; 145 attrdircookie *cookie = (attrdircookie *)_cookie; 146 uint32 numEntries = 0; 147 status_t result = B_NO_ERROR; 148 149 if (cookie->ctx == NULL) 150 panic("cookie->ctx == NULL"); 151 152 LOCK_VOL(ns); 153 154 TRACE("%s - ENTER\n", __FUNCTION__); 155 156 while (!(result = ntfs_attrs_walk(cookie->ctx))) { 157 ATTR_RECORD *attr = cookie->ctx->attr; 158 if (attr->type == AT_DATA) { 159 const char *real_name; 160 // it's the actual file body 161 if (attr->name_length == 0) 162 continue; 163 164 name = ntfs_attr_name_get((const ntfschar *)(((char *)attr) 165 + attr->name_offset), attr->name_length); 166 167 if(strncmp(name, kHaikuAttrPrefix, strlen(kHaikuAttrPrefix)) !=0 ) { 168 TRACE("found AT_DATA '%s' - Skip\n", name); 169 continue; 170 } 171 TRACE("found AT_DATA '%s' - Found\n", name); 172 173 real_name = name + strlen(kHaikuAttrPrefix); 174 175 bufsize = MIN(bufsize, sizeof(struct dirent) + strlen(real_name) + 1); 176 entry->d_ino = node->vnid; 177 entry->d_dev = ns->id; 178 entry->d_reclen = sizeof(struct dirent) + strlen(real_name); 179 //XXX size 180 strcpy(entry->d_name, real_name); 181 ntfs_attr_name_free(&name); 182 numEntries++; 183 if (numEntries >= *num) 184 break; 185 break; 186 } 187 } 188 if (result && errno != ENOENT) { 189 result = errno; 190 goto exit; 191 } else 192 result = B_OK; 193 194exit: 195 196 TRACE("%s - EXIT, result is %s, *num %d\n", __FUNCTION__, 197 strerror(result), *num); 198 199 UNLOCK_VOL(ns); 200 201 if (result) 202 *num = 0; 203 else 204 *num = numEntries; 205 206 return result; 207} 208 209 210status_t 211fs_create_attrib(fs_volume *_vol, fs_vnode *_node, const char* name, 212 uint32 type, int openMode, void** _cookie) 213{ 214 nspace *ns = (nspace*)_vol->private_volume; 215 vnode *node = (vnode*)_node->private_node; 216 attrcookie *cookie = NULL; 217 ntfschar *uname = NULL; 218 int ulen; 219 ntfs_inode *ni = NULL; 220 ntfs_attr *na = NULL; 221 status_t result = B_NO_ERROR; 222 223 if (ns->flags & B_FS_IS_READONLY) { 224 return B_READ_ONLY_DEVICE; 225 } 226 227 TRACE("%s - ENTER - name: [%s] vnid: %d\n", __FUNCTION__, name, node->vnid); 228 229 LOCK_VOL(ns); 230 231 if (node == NULL) { 232 result = EINVAL; 233 goto exit; 234 } 235 236 ni = ntfs_inode_open(ns->ntvol, node->vnid); 237 if (ni == NULL) { 238 result = errno; 239 TRACE("%s - inode_open: %s\n", __FUNCTION__, strerror(result)); 240 goto exit; 241 } 242 243 // UXA demangling TODO 244 245 // check for EA first... TODO: WRITEME 246 247 248 // check for a named stream 249 if (true) { 250 char ntfs_attr_name[MAX_PATH] = {0}; 251 strcat(ntfs_attr_name, kHaikuAttrPrefix); 252 strcat(ntfs_attr_name,name); 253 254 uname = ntfs_calloc(MAX_PATH); 255 ulen = ntfs_mbstoucs(ntfs_attr_name, &uname); 256 if (ulen < 0) { 257 result = EILSEQ; 258 TRACE("%s - mb alloc: %s\n", __FUNCTION__, strerror(result)); 259 goto exit; 260 } 261 262 na = ntfs_attr_open(ni, AT_DATA, uname, ulen); 263 if (na != NULL) { 264 if (ntfs_attr_truncate(na, 0)) { 265 result = errno; 266 goto exit; 267 } 268 } else { 269 if (ntfs_attr_add(ni, AT_DATA, uname, ulen, NULL, 0) < 0) { 270 result = errno; 271 TRACE("%s - ntfs_attr_add: %s\n", __FUNCTION__, 272 strerror(result)); 273 goto exit; 274 } 275 na = ntfs_attr_open(ni, AT_DATA, uname, ulen); 276 if (na == NULL) { 277 result = errno; 278 TRACE("%s - ntfs_attr_open: %s\n", __FUNCTION__, 279 strerror(result)); 280 goto exit; 281 } 282 } 283 if(ntfs_attr_pwrite(na, 0, sizeof(uint32), &type) < 0 ) { 284 result = errno; 285 goto exit; 286 } 287 } 288 289 cookie = (attrcookie*)ntfs_calloc(sizeof(attrcookie)); 290 291 if (cookie != NULL) { 292 cookie->omode = openMode; 293 *_cookie = (void*)cookie; 294 cookie->vnid = node->vnid; 295 cookie->uname = uname; 296 cookie->uname_len = ulen; 297 cookie->type = type; 298 uname = NULL; 299 } else 300 result = ENOMEM; 301 302exit: 303 if (uname != NULL) 304 free(uname); 305 306 if (na != NULL) 307 ntfs_attr_close(na); 308 309 if (ni != NULL) 310 ntfs_inode_close(ni); 311 312 TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result)); 313 314 UNLOCK_VOL(ns); 315 316 return result; 317} 318 319 320status_t 321fs_open_attrib(fs_volume *_vol, fs_vnode *_node, const char *name, 322 int openMode, void **_cookie) 323{ 324 nspace *ns = (nspace*)_vol->private_volume; 325 vnode *node = (vnode*)_node->private_node; 326 attrcookie *cookie = NULL; 327 ntfschar *uname = NULL; 328 int ulen; 329 ntfs_inode *ni = NULL; 330 ntfs_attr *na = NULL; 331 status_t result = B_NO_ERROR; 332 uint32 type = B_XATTR_TYPE; 333 334 TRACE("%s - ENTER - name: [%s] vnid: %d\n", __FUNCTION__, name, node->vnid); 335 336 LOCK_VOL(ns); 337 338 if (node == NULL) { 339 result = EINVAL; 340 goto exit; 341 } 342 343 ni = ntfs_inode_open(ns->ntvol, node->vnid); 344 if (ni == NULL) { 345 result = errno; 346 goto exit; 347 } 348 349 // UXA demangling TODO 350 351 // check for EA first... TODO: WRITEME 352 353 354 // check for a named stream 355 if (true) { 356 char ntfs_attr_name[MAX_PATH] = {0}; 357 strcat(ntfs_attr_name, kHaikuAttrPrefix); 358 strcat(ntfs_attr_name, name); 359 360 uname = ntfs_calloc(MAX_PATH); 361 ulen = ntfs_mbstoucs(ntfs_attr_name, &uname); 362 if (ulen < 0) { 363 result = EILSEQ; 364 goto exit; 365 } 366 367 na = ntfs_attr_open(ni, AT_DATA, uname, ulen); 368 if (na != NULL) { 369 if (openMode & O_TRUNC) { 370 if (ns->flags & B_FS_IS_READONLY) { 371 result = B_READ_ONLY_DEVICE; 372 goto exit; 373 } else { 374 if (ntfs_attr_truncate(na, sizeof(uint32))) { 375 result = errno; 376 goto exit; 377 } 378 } 379 } 380 if (ntfs_attr_pread(na, 0, sizeof(uint32), &type) != sizeof(uint32)) { 381 result = errno; 382 goto exit; 383 } 384 } else { 385 result = ENOENT; 386 goto exit; 387 } 388 } 389 390 391 cookie = (attrcookie*)ntfs_calloc(sizeof(attrcookie)); 392 393 if (cookie != NULL) { 394 cookie->omode = openMode; 395 cookie->vnid = node->vnid; 396 cookie->uname = uname; 397 cookie->uname_len = ulen; 398 cookie->type = type; 399 *_cookie = (void*)cookie; 400 uname = NULL; 401 } else 402 result = ENOMEM; 403 404exit: 405 if (uname != NULL) 406 free(uname); 407 408 if (na != NULL) 409 ntfs_attr_close(na); 410 411 if (ni != NULL) 412 ntfs_inode_close(ni); 413 414 TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result)); 415 416 UNLOCK_VOL(ns); 417 418 return result; 419} 420 421 422status_t 423fs_close_attrib(fs_volume *_vol, fs_vnode *_node, void *cookie) 424{ 425 TRACE("%s vnid: %d\n", __FUNCTION__, ((vnode*)_node->private_node)->vnid); 426 return B_NO_ERROR; 427} 428 429 430status_t 431fs_free_attrib_cookie(fs_volume *_vol, fs_vnode *_node, void *_cookie) 432{ 433 nspace *ns = (nspace*)_vol->private_volume; 434 attrcookie *cookie = (attrcookie *)_cookie; 435 436 LOCK_VOL(ns); 437 438 if (cookie->uname != NULL) 439 free(cookie->uname); 440 441 UNLOCK_VOL(ns); 442 443 free(cookie); 444 return B_NO_ERROR; 445} 446 447 448status_t 449fs_read_attrib_stat(fs_volume *_vol, fs_vnode *_node, void *_cookie, 450 struct stat *stat) 451{ 452 nspace *ns = (nspace *)_vol->private_volume; 453 vnode *node = (vnode *)_node->private_node; 454 attrcookie *cookie = (attrcookie *)_cookie; 455 ntfs_inode *ni = NULL; 456 ntfs_attr *na = NULL; 457 status_t result = B_NO_ERROR; 458 459 LOCK_VOL(ns); 460 461 ni = ntfs_inode_open(ns->ntvol, node->vnid); 462 if (ni == NULL) { 463 result = errno; 464 goto exit; 465 } 466 na = ntfs_attr_open(ni, AT_DATA, cookie->uname, cookie->uname_len); 467 if (na == NULL) { 468 result = errno; 469 goto exit; 470 } 471 472 stat->st_type = cookie->type; 473 stat->st_size = na ? na->data_size - sizeof(uint32) : 0; 474 475exit: 476 if (na != NULL) 477 ntfs_attr_close(na); 478 if (ni != NULL) 479 ntfs_inode_close(ni); 480 481 UNLOCK_VOL(ns); 482 483 return B_NO_ERROR; 484} 485 486 487status_t 488fs_read_attrib(fs_volume *_vol, fs_vnode *_node, void *_cookie, off_t pos, 489 void *buffer, size_t *len) 490{ 491 nspace *ns = (nspace *)_vol->private_volume; 492 vnode *node = (vnode *)_node->private_node; 493 attrcookie *cookie = (attrcookie *)_cookie; 494 ntfs_inode *ni = NULL; 495 ntfs_attr *na = NULL; 496 size_t size = *len; 497 int total = 0; 498 status_t result = B_NO_ERROR; 499 500 if (pos < 0) { 501 *len = 0; 502 return EINVAL; 503 } 504 505 LOCK_VOL(ns); 506 507 TRACE("%s - ENTER vnid: %d\n", __FUNCTION__, node->vnid); 508 509 ni = ntfs_inode_open(ns->ntvol, node->vnid); 510 if (ni == NULL) { 511 result = errno; 512 goto exit; 513 } 514 na = ntfs_attr_open(ni, AT_DATA, cookie->uname, cookie->uname_len); 515 if (na == NULL) { 516 result = errno; 517 goto exit; 518 } 519 520 pos += sizeof(uint32); 521 522 // it is a named stream 523 if (na != NULL) { 524 if (pos + size > na->data_size) 525 size = na->data_size - pos; 526 527 while (size) { 528 off_t bytesRead = ntfs_attr_pread(na, pos, size, buffer); 529 if (bytesRead < (s64)size) { 530 ERROR("ntfs_attr_pread returned less bytes than " 531 "requested.\n"); 532 } 533 if (bytesRead <= 0) { 534 *len = 0; 535 result = EINVAL; 536 goto exit; 537 } 538 size -= bytesRead; 539 pos += bytesRead; 540 total += bytesRead; 541 } 542 543 *len = total; 544 } else { 545 *len = 0; 546 result = ENOENT; // TODO 547 } 548 549exit: 550 if (na != NULL) 551 ntfs_attr_close(na); 552 if (ni != NULL) 553 ntfs_inode_close(ni); 554 555 TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result)); 556 557 UNLOCK_VOL(ns); 558 559 return result; 560} 561 562 563status_t 564fs_write_attrib(fs_volume *_vol, fs_vnode *_node, void *_cookie, off_t pos, 565 const void *buffer, size_t *_length) 566{ 567 nspace *ns = (nspace *)_vol->private_volume; 568 vnode *node = (vnode *)_node->private_node; 569 attrcookie *cookie = (attrcookie *)_cookie; 570 ntfs_inode *ni = NULL; 571 ntfs_attr *na = NULL; 572 size_t size = *_length; 573 char *attr_name = NULL; 574 char *real_name = NULL; 575 int total = 0; 576 status_t result = B_NO_ERROR; 577 578 TRACE("%s - ENTER vnode: %d\n", __FUNCTION__, node->vnid); 579 580 if (ns->flags & B_FS_IS_READONLY) { 581 return B_READ_ONLY_DEVICE; 582 } 583 584 if (pos < 0) { 585 *_length = 0; 586 return EINVAL; 587 } 588 589 LOCK_VOL(ns); 590 591 ni = ntfs_inode_open(ns->ntvol, node->vnid); 592 if (ni == NULL) { 593 result = errno; 594 goto exit; 595 } 596 na = ntfs_attr_open(ni, AT_DATA, cookie->uname, cookie->uname_len); 597 if (na == NULL) { 598 result = errno; 599 goto exit; 600 } 601 602 pos += sizeof(uint32); 603 604 // it is a named stream 605 if (na != NULL) { 606 if (cookie->omode & O_APPEND) 607 pos = na->data_size; 608 609 if (pos + size > na->data_size) { 610 ntfs_mark_free_space_outdated(ns); 611 if (ntfs_attr_truncate(na, pos + size)) 612 size = na->data_size - pos; 613 else 614 notify_stat_changed(ns->id, MREF(ni->mft_no), B_STAT_SIZE); 615 } 616 617 while (size) { 618 off_t bytesWritten = ntfs_attr_pwrite(na, pos, size, buffer); 619 if (bytesWritten < (s64)size) 620 ERROR("%s - ntfs_attr_pwrite returned less bytes than " 621 "requested.\n", __FUNCTION__); 622 if (bytesWritten <= 0) { 623 ERROR("%s - ntfs_attr_pwrite()<=0\n", __FUNCTION__); 624 *_length = 0; 625 result = EINVAL; 626 goto exit; 627 } 628 size -= bytesWritten; 629 pos += bytesWritten; 630 total += bytesWritten; 631 } 632 633 *_length = total; 634 } else { 635 *_length = 0; 636 result = EINVAL; 637 goto exit; 638 } 639 640 if (ntfs_ucstombs(na->name, na->name_len, &attr_name, 0) >= 0) { 641 if (attr_name != NULL) { 642 if(strncmp(attr_name, kHaikuAttrPrefix, strlen(kHaikuAttrPrefix)) !=0 ) 643 goto exit; 644 real_name = attr_name + strlen(kHaikuAttrPrefix); 645 notify_attribute_changed(ns->id, MREF(ni->mft_no), 646 real_name, B_ATTR_CHANGED); 647 free(attr_name); 648 } 649 } 650 651exit: 652 if (na != NULL) 653 ntfs_attr_close(na); 654 if (ni != NULL) 655 ntfs_inode_close(ni); 656 657 TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result)); 658 659 UNLOCK_VOL(ns); 660 661 return result; 662} 663 664 665status_t 666fs_remove_attrib(fs_volume *_vol, fs_vnode *_node, const char* name) 667{ 668 nspace *ns = (nspace *)_vol->private_volume; 669 vnode *node = (vnode *)_node->private_node; 670 char ntfs_attr_name[MAX_PATH]={0}; 671 ntfschar *uname = NULL; 672 int ulen; 673 ntfs_inode *ni = NULL; 674 status_t result = B_NO_ERROR; 675 676 TRACE("%s - ENTER - name: [%s]\n", __FUNCTION__, name); 677 678 if (ns->flags & B_FS_IS_READONLY) { 679 ERROR("ntfs is read-only\n"); 680 return B_READ_ONLY_DEVICE; 681 } 682 683 LOCK_VOL(ns); 684 685 if (node == NULL) { 686 result = EINVAL; 687 goto exit; 688 } 689 690 ni = ntfs_inode_open(ns->ntvol, node->vnid); 691 if (ni == NULL) { 692 result = errno; 693 goto exit; 694 } 695 696 strcat(ntfs_attr_name, kHaikuAttrPrefix); 697 strcat(ntfs_attr_name, name); 698 699 uname = ntfs_calloc(MAX_PATH); 700 ulen = ntfs_mbstoucs(ntfs_attr_name, &uname); 701 if (ulen < 0) { 702 result = EILSEQ; 703 goto exit; 704 } 705 706 if (ntfs_attr_remove(ni, AT_DATA, uname, ulen)) { 707 result = ENOENT; 708 goto exit; 709 } 710 711 if (!(ni->flags & FILE_ATTR_ARCHIVE)) { 712 ni->flags |= FILE_ATTR_ARCHIVE; 713 NInoFileNameSetDirty(ni); 714 } 715 notify_attribute_changed(ns->id, MREF(ni->mft_no), name, B_ATTR_REMOVED); 716exit: 717 if (uname != NULL) 718 free(uname); 719 720 if (ni != NULL) 721 ntfs_inode_close(ni); 722 723 TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result)); 724 725 UNLOCK_VOL(ns); 726 727 return result; 728} 729