1 2 #ifdef BUILDING_FS_SHELL 3 # include "compat.h" 4 # define B_OK 0 5 # define B_BAD_VALUE EINVAL 6 # define B_FILE_ERROR EBADF 7 #else 8 # include <BeOSBuildCompatibility.h> 9 # include <syscalls.h> 10 #endif 11 12 // Defined, if the host platform has extended attribute support. 13 // Unfortunately I don't seem to have extended attribute support under 14 // SuSE Linux 9.2 (kernel 2.6.8-...) with ReiserFS 3.6. 15 //#define HAS_EXTENDED_ATTRIBUTE_SUPPORT 1 16 17 #include <dirent.h> 18 #include <errno.h> 19 #include <fcntl.h> 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <unistd.h> 23 #include <sys/stat.h> 24 25 #include <string> 26 27 #include <fs_attr.h> 28 29 #include "fs_attr_impl.h" 30 31 #ifdef HAS_EXTENDED_ATTRIBUTE_SUPPORT 32 #include <sys/xattr.h> 33 34 static const char *kAttributeDirMarkAttributeName = "_has_haiku_attr_dir"; 35 #endif 36 37 using namespace std; 38 using namespace BPrivate; 39 40 static const char *sAttributeDirBasePath = HAIKU_BUILD_ATTRIBUTES_DIR; 41 42 // init_attribute_dir_base_dir 43 static status_t 44 init_attribute_dir_base_dir() 45 { 46 static bool initialized = false; 47 static status_t initError; 48 49 if (initialized) 50 return initError; 51 52 // stat the dir 53 struct stat st; 54 initError = B_OK; 55 if (lstat(sAttributeDirBasePath, &st) == 0) { 56 if (!S_ISDIR(st.st_mode)) { 57 // the attribute dir base dir is no directory 58 fprintf(stderr, "init_attribute_dir_base_dir(): The Attribute " 59 "directory base directory exists, but is no directory!\n"); 60 initError = B_FILE_ERROR; 61 } 62 63 } else { 64 // doesn't exist yet: create it 65 if (mkdir(sAttributeDirBasePath, S_IRWXU | S_IRWXG | S_IRWXO) < 0) 66 initError = errno; 67 } 68 69 initialized = true; 70 return initError; 71 } 72 73 // escape_attr_name 74 static string 75 escape_attr_name(const char *name) 76 { 77 string escapedName("_"); 78 while (*name != '\0') { 79 // we replace '/' with "_s" and '_' with "__" 80 if (*name == '/') 81 escapedName += "_s"; 82 else if (*name == '_') 83 escapedName += "__"; 84 else 85 escapedName += *name; 86 87 name++; 88 } 89 90 return escapedName; 91 } 92 93 // deescape_attr_name 94 static string 95 deescape_attr_name(const char *name) 96 { 97 if (name[0] != '_') { 98 debugger("deescape_attr_name(): name doesn't start with '_'!\n"); 99 return "___"; 100 } 101 name++; 102 103 string deescapedName; 104 while (*name != '\0') { 105 if (*name == '_') { 106 name++; 107 if (*name == 's') { 108 deescapedName += '/'; 109 } else if (*name == '_') { 110 deescapedName += '_'; 111 } else { 112 debugger("deescape_attr_name(): name contains invalid escaped " 113 "sequence!\n"); 114 name--; 115 } 116 } else 117 deescapedName += *name; 118 119 name++; 120 } 121 122 return deescapedName; 123 } 124 125 // get_attribute_dir_path 126 static string 127 get_attribute_dir_path(NodeRef ref) 128 { 129 string attrDirPath(sAttributeDirBasePath); 130 char buffer[32]; 131 sprintf(buffer, "/%lld", (int64)ref.node); 132 attrDirPath += buffer; 133 return attrDirPath; 134 } 135 136 // has_attribute_dir_mark 137 static bool 138 has_attribute_dir_mark(const char *path, int fd) 139 { 140 #ifdef HAS_EXTENDED_ATTRIBUTE_SUPPORT 141 142 uint8 value; 143 if (path) { 144 return (lgetxattr(path, kAttributeDirMarkAttributeName, &value, 1) 145 > 0); 146 } else { 147 return (fgetxattr(fd, kAttributeDirMarkAttributeName, &value, 1) 148 > 0); 149 } 150 151 #else // !HAS_EXTENDED_ATTRIBUTE_SUPPORT 152 153 // No extended attribute support. We can't mark the file and thus 154 // have to handle it as marked. 155 return true; 156 157 #endif 158 } 159 160 // set_attribute_dir_mark 161 static status_t 162 set_attribute_dir_mark(const char *path, int fd) 163 { 164 #ifdef HAS_EXTENDED_ATTRIBUTE_SUPPORT 165 166 uint8 value = 1; 167 if (path) { 168 if (lsetxattr(path, kAttributeDirMarkAttributeName, &value, 1, 0) 169 < 0) { 170 fprintf(stderr, "set_attribute_dir_mark(): lsetxattr() " 171 "failed on \"%s\"\n", path); 172 return errno; 173 } 174 } else { 175 if (fsetxattr(fd, kAttributeDirMarkAttributeName, &value, 1, 0) 176 < 0) { 177 fprintf(stderr, "set_attribute_dir_mark(): fsetxattr() " 178 "failed on FD \"%d\"\n", fd); 179 return errno; 180 } 181 } 182 183 #endif 184 185 return B_OK; 186 } 187 188 // empty_attribute_dir 189 static status_t 190 empty_attribute_dir(const char *path) 191 { 192 DIR *dir = opendir(path); 193 if (!dir) 194 return errno; 195 196 while (dirent *entry = readdir(dir)) { 197 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) 198 continue; 199 200 string entryPath(path); 201 entryPath += '/'; 202 entryPath += entry->d_name; 203 204 // We assume the attribute dir contains files only (we only create 205 // files) and thus use unlink(). 206 if (unlink(entryPath.c_str()) < 0) { 207 status_t error = errno; 208 closedir(dir); 209 return error; 210 } 211 } 212 213 closedir(dir); 214 return B_OK; 215 } 216 217 // ensure_attribute_dir_exists 218 static status_t 219 ensure_attribute_dir_exists(NodeRef ref, const char *path, int fd) 220 { 221 // init the base directory here 222 status_t error = init_attribute_dir_base_dir(); 223 if (error != B_OK) 224 return error; 225 226 // stat the dir 227 string attrDirPath(get_attribute_dir_path(ref)); 228 struct stat st; 229 if (lstat(attrDirPath.c_str(), &st) == 0) { 230 if (!S_ISDIR(st.st_mode)) { 231 // the attribute dir is no directory 232 fprintf(stderr, "ensure_attribute_dir_exists(): Attribute " 233 "directory for node %lld exists, but is no directory!\n", 234 ref.node); 235 return B_FILE_ERROR; 236 } 237 238 // already exists: Check whether the file is marked. If not, this 239 // is a stale attribute directory from a deleted node that had the 240 // same node ID as this one. 241 if (has_attribute_dir_mark(path, fd)) 242 return B_OK; 243 244 // empty the attribute dir 245 error = empty_attribute_dir(attrDirPath.c_str()); 246 if (error != B_OK) { 247 fprintf(stderr, "ensure_attribute_dir_exists(): Attribute " 248 "directory for node %lld exists, the node has no mark, and " 249 "emptying the attribute directory failed\n", 250 ref.node); 251 return error; 252 } 253 254 // mark the file 255 return set_attribute_dir_mark(path, fd); 256 } 257 258 // doesn't exist yet: create it 259 if (mkdir(attrDirPath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) < 0) 260 return errno; 261 262 // mark the file 263 return set_attribute_dir_mark(path, fd); 264 } 265 266 // open_attr_dir 267 DIR * 268 BPrivate::open_attr_dir(NodeRef ref, const char *path, int fd) 269 { 270 // make sure the directory exists 271 status_t error = ensure_attribute_dir_exists(ref, path, fd); 272 if (error != B_OK) { 273 errno = error; 274 return NULL; 275 } 276 277 // open it 278 string dirPath(get_attribute_dir_path(ref)); 279 return opendir(dirPath.c_str()); 280 } 281 282 // get_attribute_path 283 status_t 284 BPrivate::get_attribute_path(NodeRef ref, const char *path, int fd, 285 const char *attribute, string &attrPath, string &typePath) 286 { 287 if (!attribute || strlen(attribute) == 0) 288 return B_BAD_VALUE; 289 290 // make sure the attribute dir for the node exits 291 status_t error = ensure_attribute_dir_exists(ref, path, fd); 292 if (error != B_OK) { 293 errno = error; 294 return -1; 295 } 296 297 // construct the attribute path 298 attrPath = get_attribute_dir_path(ref) + '/'; 299 string attrName(escape_attr_name(attribute)); 300 typePath = attrPath + "t" + attrName; 301 attrPath += attrName; 302 303 return B_OK; 304 } 305 306 // get_attribute_path 307 static status_t 308 get_attribute_path(int fd, const char *attribute, string &attrPath, 309 string &typePath) 310 { 311 // stat the file to get a NodeRef 312 struct stat st; 313 if (fstat(fd, &st) < 0) 314 return errno; 315 NodeRef ref(st); 316 317 return get_attribute_path(ref, NULL, fd, attribute, attrPath, typePath); 318 } 319 320 // fs_open_attr_dir 321 DIR * 322 fs_open_attr_dir(const char *path) 323 { 324 struct stat st; 325 if (lstat(path, &st)) 326 return NULL; 327 328 return open_attr_dir(NodeRef(st), path, -1); 329 } 330 331 // fs_fopen_attr_dir 332 DIR * 333 fs_fopen_attr_dir(int fd) 334 { 335 struct stat st; 336 337 #ifdef BUILDING_FS_SHELL 338 339 if (fstat(fd, &st) < 0) 340 return NULL; 341 342 return open_attr_dir(NodeRef(st), NULL, fd); 343 344 #else 345 346 status_t error = _kern_read_stat(fd, NULL, false, &st, 347 sizeof(struct stat)); 348 if (error != B_OK) { 349 errno = error; 350 return NULL; 351 } 352 353 // Try to get a path. If we can't get a path, this is must be a "real" 354 // (i.e. system) file descriptor, which is just as well. 355 string path; 356 bool pathValid = (get_path(fd, NULL, path) == B_OK); 357 358 // get the attribute path 359 return open_attr_dir(NodeRef(st), (pathValid ? path.c_str() : NULL), 360 (pathValid ? -1 : fd)); 361 362 #endif 363 } 364 365 // fs_close_attr_dir 366 int 367 fs_close_attr_dir(DIR *dir) 368 { 369 return closedir(dir); 370 } 371 372 // fs_read_attr_dir 373 struct dirent * 374 fs_read_attr_dir(DIR *dir) 375 { 376 struct dirent *entry = NULL; 377 while (true) { 378 // read the next entry 379 entry = readdir(dir); 380 if (!entry) 381 return NULL; 382 383 // ignore administrative entries; the 384 if (entry->d_name[0] == '_') { 385 string attrName = deescape_attr_name(entry->d_name); 386 strcpy(entry->d_name, attrName.c_str()); 387 return entry; 388 } 389 } 390 } 391 392 // fs_rewind_attr_dir 393 void 394 fs_rewind_attr_dir(DIR *dir) 395 { 396 rewinddir(dir); 397 } 398 399 // fs_open_attr 400 int 401 fs_open_attr(int fd, const char *attribute, uint32 type, int openMode) 402 { 403 if (!attribute) { 404 errno = B_BAD_VALUE; 405 return -1; 406 } 407 408 // get the attribute path 409 string attrPath; 410 string typePath; 411 status_t error = get_attribute_path(fd, attribute, attrPath, typePath); 412 if (error != B_OK) { 413 errno = error; 414 return -1; 415 } 416 417 // check, if the attribute already exists 418 struct stat st; 419 bool exists = (lstat(attrPath.c_str(), &st) == 0); 420 421 // open the attribute 422 int attrFD = open(attrPath.c_str(), openMode, S_IRWXU | S_IRWXG | S_IRWXO); 423 if (attrFD < 0) 424 return -1; 425 426 // set the type, if the attribute didn't exist yet 427 if (!exists) { 428 // create a file prefixed "t" 429 int typeFD = creat(typePath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO); 430 if (typeFD >= 0) { 431 // write the type into the file 432 if (write(typeFD, &type, sizeof(type)) < 0) 433 error = errno; 434 435 close(typeFD); 436 437 } else 438 error = errno; 439 440 // remove type and attribute file, if something went wrong 441 if (error != B_OK) { 442 if (typeFD > 0) { 443 unlink(typePath.c_str()); 444 } 445 446 close(attrFD); 447 unlink(attrPath.c_str()); 448 449 errno = error; 450 return -1; 451 } 452 } 453 454 return attrFD; 455 } 456 457 // fs_close_attr 458 int 459 fs_close_attr(int fd) 460 { 461 return close(fd); 462 } 463 464 // fs_read_attr 465 ssize_t 466 fs_read_attr(int fd, const char *attribute, uint32 type, off_t pos, 467 void *buffer, size_t readBytes) 468 { 469 // open the attribute 470 int attrFD = fs_open_attr(fd, attribute, type, O_RDONLY); 471 if (attrFD < 0) 472 return attrFD; 473 474 // read 475 ssize_t bytesRead = read_pos(attrFD, pos, buffer, readBytes); 476 status_t error = errno; 477 478 // close the attribute 479 fs_close_attr(attrFD); 480 481 if (bytesRead < 0) { 482 errno = error; 483 return -1; 484 } 485 486 return bytesRead; 487 } 488 489 // fs_write_attr 490 ssize_t 491 fs_write_attr(int fd, const char *attribute, uint32 type, off_t pos, 492 const void *buffer, size_t readBytes) 493 { 494 // open the attribute 495 int attrFD = fs_open_attr(fd, attribute, type, 496 O_WRONLY | O_CREAT | O_TRUNC); 497 if (attrFD < 0) 498 return attrFD; 499 500 // read 501 ssize_t bytesWritten = write_pos(attrFD, pos, buffer, readBytes); 502 status_t error = errno; 503 504 // close the attribute 505 fs_close_attr(attrFD); 506 507 if (bytesWritten < 0) { 508 errno = error; 509 return -1; 510 } 511 512 return bytesWritten; 513 } 514 515 // fs_remove_attr 516 int 517 fs_remove_attr(int fd, const char *attribute) 518 { 519 if (!attribute) { 520 errno = B_BAD_VALUE; 521 return -1; 522 } 523 524 // get the attribute path 525 string attrPath; 526 string typePath; 527 status_t error = get_attribute_path(fd, attribute, attrPath, typePath); 528 if (error != B_OK) { 529 errno = error; 530 return -1; 531 } 532 533 // remove the attribute 534 if (unlink(attrPath.c_str()) < 0) 535 return -1; 536 537 unlink(typePath.c_str()); 538 539 return B_OK; 540 } 541 542 // fs_stat_attr 543 int 544 fs_stat_attr(int fd, const char *attribute, struct attr_info *attrInfo) 545 { 546 if (!attribute || !attrInfo) { 547 errno = B_BAD_VALUE; 548 return -1; 549 } 550 551 // get the attribute path 552 string attrPath; 553 string typePath; 554 status_t error = get_attribute_path(fd, attribute, attrPath, typePath); 555 if (error != B_OK) { 556 errno = error; 557 return -1; 558 } 559 560 // stat the attribute file to get the size of the attribute 561 struct stat st; 562 if (lstat(attrPath.c_str(), &st) < 0) 563 return -1; 564 565 attrInfo->size = st.st_size; 566 567 // now open the attribute type file and read the attribute's type 568 int typeFD = open(typePath.c_str(), O_RDONLY); 569 if (typeFD < 0) 570 return -1; 571 572 ssize_t bytesRead = read(typeFD, &attrInfo->type, sizeof(attrInfo->type)); 573 if (bytesRead < 0) 574 error = errno; 575 else if (bytesRead < (ssize_t)sizeof(attrInfo->type)) 576 error = B_FILE_ERROR; 577 578 close(typeFD); 579 580 // fail on error 581 if (error != B_OK) { 582 errno = error; 583 return -1; 584 } 585 586 return 0; 587 } 588 589