1 /* 2 * Copyright 2002-2011 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Tyler Dauwalder 7 * Ingo Weinhold, bonefish@users.sf.net 8 */ 9 10 11 #include <Node.h> 12 13 #include <errno.h> 14 #include <fcntl.h> 15 #include <new> 16 #include <string.h> 17 #include <unistd.h> 18 19 #include <Directory.h> 20 #include <Entry.h> 21 #include <fs_attr.h> 22 #include <String.h> 23 #include <TypeConstants.h> 24 25 #include <syscalls.h> 26 27 #include "storage_support.h" 28 29 30 // #pragma mark - node_ref 31 32 33 node_ref::node_ref() 34 : 35 device((dev_t)-1), 36 node((ino_t)-1) 37 { 38 } 39 40 41 node_ref::node_ref(const node_ref& other) 42 : 43 device((dev_t)-1), 44 node((ino_t)-1) 45 { 46 *this = other; 47 } 48 49 50 bool 51 node_ref::operator==(const node_ref& other) const 52 { 53 return (device == other.device && node == other.node); 54 } 55 56 57 bool 58 node_ref::operator!=(const node_ref& other) const 59 { 60 return !(*this == other); 61 } 62 63 64 bool 65 node_ref::operator<(const node_ref& other) const 66 { 67 if (this->device != other.device) 68 return this->device < other.device; 69 70 return this->node < other.node; 71 } 72 73 74 node_ref& 75 node_ref::operator=(const node_ref& other) 76 { 77 device = other.device; 78 node = other.node; 79 return *this; 80 } 81 82 83 // #pragma mark - BNode 84 85 86 BNode::BNode() 87 : 88 fFd(-1), 89 fAttrFd(-1), 90 fCStatus(B_NO_INIT) 91 { 92 } 93 94 95 BNode::BNode(const entry_ref* ref) 96 : 97 fFd(-1), 98 fAttrFd(-1), 99 fCStatus(B_NO_INIT) 100 { 101 // fCStatus is set by SetTo(), ignore return value 102 (void)SetTo(ref); 103 } 104 105 106 BNode::BNode(const BEntry* entry) 107 : 108 fFd(-1), 109 fAttrFd(-1), 110 fCStatus(B_NO_INIT) 111 { 112 // fCStatus is set by SetTo(), ignore return value 113 (void)SetTo(entry); 114 } 115 116 117 BNode::BNode(const char* path) 118 : 119 fFd(-1), 120 fAttrFd(-1), 121 fCStatus(B_NO_INIT) 122 { 123 // fCStatus is set by SetTo(), ignore return value 124 (void)SetTo(path); 125 } 126 127 128 BNode::BNode(const BDirectory* dir, const char* path) 129 : 130 fFd(-1), 131 fAttrFd(-1), 132 fCStatus(B_NO_INIT) 133 { 134 // fCStatus is set by SetTo(), ignore return value 135 (void)SetTo(dir, path); 136 } 137 138 139 BNode::BNode(const BNode& node) 140 : 141 fFd(-1), 142 fAttrFd(-1), 143 fCStatus(B_NO_INIT) 144 { 145 *this = node; 146 } 147 148 149 BNode::~BNode() 150 { 151 Unset(); 152 } 153 154 155 status_t 156 BNode::InitCheck() const 157 { 158 return fCStatus; 159 } 160 161 162 status_t 163 BNode::SetTo(const entry_ref* ref) 164 { 165 return _SetTo(ref, false); 166 } 167 168 169 status_t 170 BNode::SetTo(const BEntry* entry) 171 { 172 if (entry == NULL) { 173 Unset(); 174 return (fCStatus = B_BAD_VALUE); 175 } 176 177 return _SetTo(entry->fDirFd, entry->fName, false); 178 } 179 180 181 status_t 182 BNode::SetTo(const char* path) 183 { 184 return _SetTo(-1, path, false); 185 } 186 187 188 status_t 189 BNode::SetTo(const BDirectory* dir, const char* path) 190 { 191 if (dir == NULL || path == NULL 192 || BPrivate::Storage::is_absolute_path(path)) { 193 Unset(); 194 return (fCStatus = B_BAD_VALUE); 195 } 196 197 return _SetTo(dir->fDirFd, path, false); 198 } 199 200 201 void 202 BNode::Unset() 203 { 204 close_fd(); 205 fCStatus = B_NO_INIT; 206 } 207 208 209 status_t 210 BNode::Lock() 211 { 212 if (fCStatus != B_OK) 213 return fCStatus; 214 215 return _kern_lock_node(fFd); 216 } 217 218 219 status_t 220 BNode::Unlock() 221 { 222 if (fCStatus != B_OK) 223 return fCStatus; 224 225 return _kern_unlock_node(fFd); 226 } 227 228 229 status_t 230 BNode::Sync() 231 { 232 return (fCStatus != B_OK) ? B_FILE_ERROR : _kern_fsync(fFd); 233 } 234 235 236 ssize_t 237 BNode::WriteAttr(const char* attr, type_code type, off_t offset, 238 const void* buffer, size_t length) 239 { 240 if (fCStatus != B_OK) 241 return B_FILE_ERROR; 242 243 if (attr == NULL || buffer == NULL) 244 return B_BAD_VALUE; 245 246 ssize_t result = fs_write_attr(fFd, attr, type, offset, buffer, length); 247 248 return result < 0 ? errno : result; 249 } 250 251 252 ssize_t 253 BNode::ReadAttr(const char* attr, type_code type, off_t offset, 254 void* buffer, size_t length) const 255 { 256 if (fCStatus != B_OK) 257 return B_FILE_ERROR; 258 259 if (attr == NULL || buffer == NULL) 260 return B_BAD_VALUE; 261 262 ssize_t result = fs_read_attr(fFd, attr, type, offset, buffer, length); 263 264 return result == -1 ? errno : result; 265 } 266 267 268 status_t 269 BNode::RemoveAttr(const char* name) 270 { 271 return fCStatus != B_OK ? B_FILE_ERROR : _kern_remove_attr(fFd, name); 272 } 273 274 275 status_t 276 BNode::RenameAttr(const char* oldName, const char* newName) 277 { 278 if (fCStatus != B_OK) 279 return B_FILE_ERROR; 280 281 return _kern_rename_attr(fFd, oldName, fFd, newName); 282 } 283 284 285 status_t 286 BNode::GetAttrInfo(const char* name, struct attr_info* info) const 287 { 288 if (fCStatus != B_OK) 289 return B_FILE_ERROR; 290 291 if (name == NULL || info == NULL) 292 return B_BAD_VALUE; 293 294 return fs_stat_attr(fFd, name, info) < 0 ? errno : B_OK ; 295 } 296 297 298 status_t 299 BNode::GetNextAttrName(char* buffer) 300 { 301 // We're allowed to assume buffer is at least 302 // B_ATTR_NAME_LENGTH chars long, but NULLs 303 // are not acceptable. 304 305 // BeOS R5 crashed when passed NULL 306 if (buffer == NULL) 307 return B_BAD_VALUE; 308 309 if (InitAttrDir() != B_OK) 310 return B_FILE_ERROR; 311 312 BPrivate::Storage::LongDirEntry longEntry; 313 struct dirent* entry = longEntry.dirent(); 314 ssize_t result = _kern_read_dir(fAttrFd, entry, sizeof(longEntry), 1); 315 if (result < 0) 316 return result; 317 318 if (result == 0) 319 return B_ENTRY_NOT_FOUND; 320 321 strlcpy(buffer, entry->d_name, B_ATTR_NAME_LENGTH); 322 323 return B_OK; 324 } 325 326 327 status_t 328 BNode::RewindAttrs() 329 { 330 if (InitAttrDir() != B_OK) 331 return B_FILE_ERROR; 332 333 return _kern_rewind_dir(fAttrFd); 334 } 335 336 337 status_t 338 BNode::WriteAttrString(const char* name, const BString* data) 339 { 340 status_t error = (!name || !data) ? B_BAD_VALUE : B_OK; 341 if (error == B_OK) { 342 int32 length = data->Length() + 1; 343 ssize_t sizeWritten = WriteAttr(name, B_STRING_TYPE, 0, data->String(), 344 length); 345 if (sizeWritten != length) 346 error = sizeWritten; 347 } 348 349 return error; 350 } 351 352 353 status_t 354 BNode::ReadAttrString(const char* name, BString* result) const 355 { 356 if (name == NULL || result == NULL) 357 return B_BAD_VALUE; 358 359 attr_info info; 360 status_t error; 361 362 error = GetAttrInfo(name, &info); 363 if (error != B_OK) 364 return error; 365 366 // Lock the string's buffer so we can meddle with it 367 char* data = result->LockBuffer(info.size + 1); 368 if (data == NULL) 369 return B_NO_MEMORY; 370 371 // Read the attribute 372 ssize_t bytes = ReadAttr(name, B_STRING_TYPE, 0, data, info.size); 373 // Check for failure 374 if (bytes < 0) { 375 error = bytes; 376 bytes = 0; 377 // In this instance, we simply clear the string 378 } else 379 error = B_OK; 380 381 // Null terminate the new string just to be sure (since it *is* 382 // possible to read and write non-NULL-terminated strings) 383 data[bytes] = 0; 384 result->UnlockBuffer(); 385 386 return error; 387 } 388 389 390 BNode& 391 BNode::operator=(const BNode& node) 392 { 393 // No need to do any assignment if already equal 394 if (*this == node) 395 return *this; 396 397 // Close down out current state 398 Unset(); 399 // We have to manually dup the node, because R5::BNode::Dup() 400 // is not declared to be const (which IMO is retarded). 401 fFd = _kern_dup(node.fFd); 402 fCStatus = (fFd < 0) ? B_NO_INIT : B_OK ; 403 404 return *this; 405 } 406 407 408 bool 409 BNode::operator==(const BNode& node) const 410 { 411 if (fCStatus == B_NO_INIT && node.InitCheck() == B_NO_INIT) 412 return true; 413 414 if (fCStatus == B_OK && node.InitCheck() == B_OK) { 415 // compare the node_refs 416 node_ref ref1, ref2; 417 if (GetNodeRef(&ref1) != B_OK) 418 return false; 419 420 if (node.GetNodeRef(&ref2) != B_OK) 421 return false; 422 423 return (ref1 == ref2); 424 } 425 426 return false; 427 } 428 429 430 bool 431 BNode::operator!=(const BNode& node) const 432 { 433 return !(*this == node); 434 } 435 436 437 int 438 BNode::Dup() 439 { 440 int fd = _kern_dup(fFd); 441 442 return (fd >= 0 ? fd : -1); 443 // comply with R5 return value 444 } 445 446 447 /*! (currently unused) */ 448 void BNode::_RudeNode1() { } 449 void BNode::_RudeNode2() { } 450 void BNode::_RudeNode3() { } 451 void BNode::_RudeNode4() { } 452 void BNode::_RudeNode5() { } 453 void BNode::_RudeNode6() { } 454 455 456 /*! Sets the node's file descriptor. 457 458 Used by each implementation (i.e. BNode, BFile, BDirectory, etc.) to set 459 the node's file descriptor. This allows each subclass to use the various 460 file-type specific system calls for opening file descriptors. 461 462 \note This method calls close_fd() to close previously opened FDs. Thus 463 derived classes should take care to first call set_fd() and set 464 class specific resources freed in their close_fd() version 465 thereafter. 466 467 \param fd the file descriptor this BNode should be set to (may be -1). 468 469 \returns \c B_OK if everything went fine, or an error code if something 470 went wrong. 471 */ 472 status_t 473 BNode::set_fd(int fd) 474 { 475 if (fFd != -1) 476 close_fd(); 477 478 fFd = fd; 479 480 return B_OK; 481 } 482 483 484 /*! Closes the node's file descriptor(s). 485 486 To be implemented by subclasses to close the file descriptor using the 487 proper system call for the given file-type. This implementation calls 488 _kern_close(fFd) and also _kern_close(fAttrDir) if necessary. 489 */ 490 void 491 BNode::close_fd() 492 { 493 if (fAttrFd >= 0) { 494 _kern_close(fAttrFd); 495 fAttrFd = -1; 496 } 497 if (fFd >= 0) { 498 _kern_close(fFd); 499 fFd = -1; 500 } 501 } 502 503 504 /*! Sets the BNode's status. 505 506 To be used by derived classes instead of accessing the BNode's private 507 \c fCStatus member directly. 508 509 \param newStatus the new value for the status variable. 510 */ 511 void 512 BNode::set_status(status_t newStatus) 513 { 514 fCStatus = newStatus; 515 } 516 517 518 /*! Initializes the BNode's file descriptor to the node referred to 519 by the given FD and path combo. 520 521 \a path must either be \c NULL, an absolute or a relative path. 522 In the first case, \a fd must not be \c NULL; the node it refers to will 523 be opened. If absolute, \a fd is ignored. If relative and \a fd is >= 0, 524 it will be reckoned off the directory identified by \a fd, otherwise off 525 the current working directory. 526 527 The method will first try to open the node with read and write permission. 528 If that fails due to a read-only FS or because the user has no write 529 permission for the node, it will re-try opening the node read-only. 530 531 The \a fCStatus member will be set to the return value of this method. 532 533 \param fd Either a directory FD or a value < 0. In the latter case \a path 534 must be specified. 535 \param path Either \a NULL in which case \a fd must be given, absolute, or 536 relative to the directory specified by \a fd (if given) or to the 537 current working directory. 538 \param traverse If the node identified by \a fd and \a path is a symlink 539 and \a traverse is \c true, the symlink will be resolved recursively. 540 541 \returns \c B_OK if everything went fine, or an error code otherwise. 542 */ 543 status_t 544 BNode::_SetTo(int fd, const char* path, bool traverse) 545 { 546 Unset(); 547 548 status_t error = (fd >= 0 || path ? B_OK : B_BAD_VALUE); 549 if (error == B_OK) { 550 int traverseFlag = (traverse ? 0 : O_NOTRAVERSE); 551 fFd = _kern_open(fd, path, O_RDWR | O_CLOEXEC | traverseFlag, 0); 552 if (fFd < B_OK && fFd != B_ENTRY_NOT_FOUND) { 553 // opening read-write failed, re-try read-only 554 fFd = _kern_open(fd, path, O_RDONLY | O_CLOEXEC | traverseFlag, 0); 555 } 556 if (fFd < 0) 557 error = fFd; 558 } 559 560 return fCStatus = error; 561 } 562 563 564 /*! Initializes the BNode's file descriptor to the node referred to 565 by the given entry_ref. 566 567 The method will first try to open the node with read and write permission. 568 If that fails due to a read-only FS or because the user has no write 569 permission for the node, it will re-try opening the node read-only. 570 571 The \a fCStatus member will be set to the return value of this method. 572 573 \param ref An entry_ref identifying the node to be opened. 574 \param traverse If the node identified by \a ref is a symlink and 575 \a traverse is \c true, the symlink will be resolved recursively. 576 577 \returns \c B_OK if everything went fine, or an error code otherwise. 578 */ 579 status_t 580 BNode::_SetTo(const entry_ref* ref, bool traverse) 581 { 582 Unset(); 583 584 status_t result = (ref ? B_OK : B_BAD_VALUE); 585 if (result == B_OK) { 586 int traverseFlag = (traverse ? 0 : O_NOTRAVERSE); 587 fFd = _kern_open_entry_ref(ref->device, ref->directory, ref->name, 588 O_RDWR | O_CLOEXEC | traverseFlag, 0); 589 if (fFd < B_OK && fFd != B_ENTRY_NOT_FOUND) { 590 // opening read-write failed, re-try read-only 591 fFd = _kern_open_entry_ref(ref->device, ref->directory, ref->name, 592 O_RDONLY | O_CLOEXEC | traverseFlag, 0); 593 } 594 if (fFd < 0) 595 result = fFd; 596 } 597 598 return fCStatus = result; 599 } 600 601 602 /*! Modifies a certain setting for this node based on \a what and the 603 corresponding value in \a st. 604 605 Inherited from and called by BStatable. 606 607 \param st a stat structure containing the value to be set. 608 \param what specifies what setting to be modified. 609 610 \returns \c B_OK if everything went fine, or an error code otherwise. 611 */ 612 status_t 613 BNode::set_stat(struct stat& stat, uint32 what) 614 { 615 if (fCStatus != B_OK) 616 return B_FILE_ERROR; 617 618 return _kern_write_stat(fFd, NULL, false, &stat, sizeof(struct stat), 619 what); 620 } 621 622 623 624 /*! Verifies that the BNode has been properly initialized, and then 625 (if necessary) opens the attribute directory on the node's file 626 descriptor, storing it in fAttrDir. 627 628 \returns \c B_OK if everything went fine, or an error code otherwise. 629 */ 630 status_t 631 BNode::InitAttrDir() 632 { 633 if (fCStatus == B_OK && fAttrFd < 0) { 634 fAttrFd = _kern_open_attr_dir(fFd, NULL); 635 if (fAttrFd < 0) 636 return fAttrFd; 637 638 // set close on exec flag 639 fcntl(fAttrFd, F_SETFD, FD_CLOEXEC); 640 } 641 642 return fCStatus; 643 } 644 645 646 status_t 647 BNode::GetStat(struct stat* stat) const 648 { 649 return fCStatus != B_OK 650 ? fCStatus 651 : _kern_read_stat(fFd, NULL, false, stat, sizeof(struct stat)); 652 } 653