1 /* 2 * Copyright (c) 2008 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * $DragonFly: src/sys/vfs/hammer/hammer_ioctl.c,v 1.32 2008/11/13 02:23:29 dillon Exp $ 35 */ 36 37 #include "hammer.h" 38 39 static int hammer_ioc_gethistory(hammer_transaction_t trans, hammer_inode_t ip, 40 struct hammer_ioc_history *hist); 41 static int hammer_ioc_synctid(hammer_transaction_t trans, hammer_inode_t ip, 42 struct hammer_ioc_synctid *std); 43 static int hammer_ioc_get_version(hammer_transaction_t trans, 44 hammer_inode_t ip, 45 struct hammer_ioc_version *ver); 46 static int hammer_ioc_set_version(hammer_transaction_t trans, 47 hammer_inode_t ip, 48 struct hammer_ioc_version *ver); 49 static int hammer_ioc_get_info(hammer_transaction_t trans, 50 struct hammer_ioc_info *info); 51 static int hammer_ioc_add_snapshot(hammer_transaction_t trans, hammer_inode_t ip, 52 struct hammer_ioc_snapshot *snap); 53 static int hammer_ioc_del_snapshot(hammer_transaction_t trans, hammer_inode_t ip, 54 struct hammer_ioc_snapshot *snap); 55 static int hammer_ioc_get_snapshot(hammer_transaction_t trans, hammer_inode_t ip, 56 struct hammer_ioc_snapshot *snap); 57 static int hammer_ioc_get_config(hammer_transaction_t trans, hammer_inode_t ip, 58 struct hammer_ioc_config *snap); 59 static int hammer_ioc_set_config(hammer_transaction_t trans, hammer_inode_t ip, 60 struct hammer_ioc_config *snap); 61 62 int 63 hammer_ioctl(hammer_inode_t ip, u_long com, caddr_t data, int fflag, 64 struct ucred *cred) 65 { 66 struct hammer_transaction trans; 67 int error; 68 69 error = priv_check_cred(cred, PRIV_HAMMER_IOCTL, 0); 70 71 hammer_start_transaction(&trans, ip->hmp); 72 73 switch(com) { 74 case HAMMERIOC_PRUNE: 75 if (error == 0) { 76 error = hammer_ioc_prune(&trans, ip, 77 (struct hammer_ioc_prune *)data); 78 } 79 break; 80 case HAMMERIOC_GETHISTORY: 81 error = hammer_ioc_gethistory(&trans, ip, 82 (struct hammer_ioc_history *)data); 83 break; 84 case HAMMERIOC_REBLOCK: 85 if (error == 0) { 86 error = hammer_ioc_reblock(&trans, ip, 87 (struct hammer_ioc_reblock *)data); 88 } 89 break; 90 case HAMMERIOC_REBALANCE: 91 if (error == 0) { 92 error = hammer_ioc_rebalance(&trans, ip, 93 (struct hammer_ioc_rebalance *)data); 94 } 95 break; 96 case HAMMERIOC_SYNCTID: 97 error = hammer_ioc_synctid(&trans, ip, 98 (struct hammer_ioc_synctid *)data); 99 break; 100 case HAMMERIOC_GET_PSEUDOFS: 101 error = hammer_ioc_get_pseudofs(&trans, ip, 102 (struct hammer_ioc_pseudofs_rw *)data); 103 break; 104 case HAMMERIOC_SET_PSEUDOFS: 105 if (error == 0) { 106 error = hammer_ioc_set_pseudofs(&trans, ip, cred, 107 (struct hammer_ioc_pseudofs_rw *)data); 108 } 109 break; 110 case HAMMERIOC_UPG_PSEUDOFS: 111 if (error == 0) { 112 error = hammer_ioc_upgrade_pseudofs(&trans, ip, 113 (struct hammer_ioc_pseudofs_rw *)data); 114 } 115 break; 116 case HAMMERIOC_DGD_PSEUDOFS: 117 if (error == 0) { 118 error = hammer_ioc_downgrade_pseudofs(&trans, ip, 119 (struct hammer_ioc_pseudofs_rw *)data); 120 } 121 break; 122 case HAMMERIOC_RMR_PSEUDOFS: 123 if (error == 0) { 124 error = hammer_ioc_destroy_pseudofs(&trans, ip, 125 (struct hammer_ioc_pseudofs_rw *)data); 126 } 127 break; 128 case HAMMERIOC_WAI_PSEUDOFS: 129 if (error == 0) { 130 error = hammer_ioc_wait_pseudofs(&trans, ip, 131 (struct hammer_ioc_pseudofs_rw *)data); 132 } 133 break; 134 case HAMMERIOC_MIRROR_READ: 135 if (error == 0) { 136 error = hammer_ioc_mirror_read(&trans, ip, 137 (struct hammer_ioc_mirror_rw *)data); 138 } 139 break; 140 case HAMMERIOC_MIRROR_WRITE: 141 if (error == 0) { 142 error = hammer_ioc_mirror_write(&trans, ip, 143 (struct hammer_ioc_mirror_rw *)data); 144 } 145 break; 146 case HAMMERIOC_GET_VERSION: 147 error = hammer_ioc_get_version(&trans, ip, 148 (struct hammer_ioc_version *)data); 149 break; 150 case HAMMERIOC_GET_INFO: 151 error = hammer_ioc_get_info(&trans, 152 (struct hammer_ioc_info *)data); 153 break; 154 case HAMMERIOC_SET_VERSION: 155 if (error == 0) { 156 error = hammer_ioc_set_version(&trans, ip, 157 (struct hammer_ioc_version *)data); 158 } 159 break; 160 case HAMMERIOC_EXPAND: 161 if (error == 0) { 162 error = priv_check_cred(cred, PRIV_HAMMER_EXPAND, 0); 163 if (error == 0) 164 error = hammer_ioc_expand(&trans, ip, 165 (struct hammer_ioc_expand *)data); 166 } 167 break; 168 case HAMMERIOC_ADD_SNAPSHOT: 169 if (error == 0) { 170 error = hammer_ioc_add_snapshot( 171 &trans, ip, (struct hammer_ioc_snapshot *)data); 172 } 173 break; 174 case HAMMERIOC_DEL_SNAPSHOT: 175 if (error == 0) { 176 error = hammer_ioc_del_snapshot( 177 &trans, ip, (struct hammer_ioc_snapshot *)data); 178 } 179 break; 180 case HAMMERIOC_GET_SNAPSHOT: 181 error = hammer_ioc_get_snapshot( 182 &trans, ip, (struct hammer_ioc_snapshot *)data); 183 break; 184 case HAMMERIOC_GET_CONFIG: 185 error = hammer_ioc_get_config( 186 &trans, ip, (struct hammer_ioc_config *)data); 187 break; 188 case HAMMERIOC_SET_CONFIG: 189 if (error == 0) { 190 error = hammer_ioc_set_config( 191 &trans, ip, (struct hammer_ioc_config *)data); 192 } 193 break; 194 default: 195 error = EOPNOTSUPP; 196 break; 197 } 198 hammer_done_transaction(&trans); 199 return (error); 200 } 201 202 /* 203 * Iterate through an object's inode or an object's records and record 204 * modification TIDs. 205 */ 206 static void add_history(hammer_inode_t ip, struct hammer_ioc_history *hist, 207 hammer_btree_elm_t elm); 208 209 static 210 int 211 hammer_ioc_gethistory(hammer_transaction_t trans, hammer_inode_t ip, 212 struct hammer_ioc_history *hist) 213 { 214 struct hammer_cursor cursor; 215 hammer_btree_elm_t elm; 216 int error; 217 218 /* 219 * Validate the structure and initialize for return. 220 */ 221 if (hist->beg_tid > hist->end_tid) 222 return(EINVAL); 223 if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) { 224 if (hist->key > hist->nxt_key) 225 return(EINVAL); 226 } 227 228 hist->obj_id = ip->obj_id; 229 hist->count = 0; 230 hist->nxt_tid = hist->end_tid; 231 hist->head.flags &= ~HAMMER_IOC_HISTORY_NEXT_TID; 232 hist->head.flags &= ~HAMMER_IOC_HISTORY_NEXT_KEY; 233 hist->head.flags &= ~HAMMER_IOC_HISTORY_EOF; 234 hist->head.flags &= ~HAMMER_IOC_HISTORY_UNSYNCED; 235 if ((ip->flags & HAMMER_INODE_MODMASK) & 236 ~(HAMMER_INODE_ATIME | HAMMER_INODE_MTIME)) { 237 hist->head.flags |= HAMMER_IOC_HISTORY_UNSYNCED; 238 } 239 240 /* 241 * Setup the cursor. We can't handle undeletable records 242 * (create_tid of 0) at the moment. A create_tid of 0 has 243 * a special meaning and cannot be specified in the cursor. 244 */ 245 error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL); 246 if (error) { 247 hammer_done_cursor(&cursor); 248 return(error); 249 } 250 251 cursor.key_beg.obj_id = hist->obj_id; 252 cursor.key_beg.create_tid = hist->beg_tid; 253 cursor.key_beg.delete_tid = 0; 254 cursor.key_beg.obj_type = 0; 255 if (cursor.key_beg.create_tid == HAMMER_MIN_TID) 256 cursor.key_beg.create_tid = 1; 257 258 cursor.key_end.obj_id = hist->obj_id; 259 cursor.key_end.create_tid = hist->end_tid; 260 cursor.key_end.delete_tid = 0; 261 cursor.key_end.obj_type = 0; 262 263 cursor.flags |= HAMMER_CURSOR_END_EXCLUSIVE; 264 265 if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) { 266 /* 267 * key-range within the file. For a regular file the 268 * on-disk key represents BASE+LEN, not BASE, so the 269 * first possible record containing the offset 'key' 270 * has an on-disk key of (key + 1). 271 */ 272 cursor.key_beg.key = hist->key; 273 cursor.key_end.key = HAMMER_MAX_KEY; 274 cursor.key_beg.localization = ip->obj_localization + 275 HAMMER_LOCALIZE_MISC; 276 cursor.key_end.localization = ip->obj_localization + 277 HAMMER_LOCALIZE_MISC; 278 279 switch(ip->ino_data.obj_type) { 280 case HAMMER_OBJTYPE_REGFILE: 281 ++cursor.key_beg.key; 282 cursor.key_beg.rec_type = HAMMER_RECTYPE_DATA; 283 break; 284 case HAMMER_OBJTYPE_DIRECTORY: 285 cursor.key_beg.rec_type = HAMMER_RECTYPE_DIRENTRY; 286 cursor.key_beg.localization = ip->obj_localization + 287 hammer_dir_localization(ip); 288 cursor.key_end.localization = ip->obj_localization + 289 hammer_dir_localization(ip); 290 break; 291 case HAMMER_OBJTYPE_DBFILE: 292 cursor.key_beg.rec_type = HAMMER_RECTYPE_DB; 293 break; 294 default: 295 error = EINVAL; 296 break; 297 } 298 cursor.key_end.rec_type = cursor.key_beg.rec_type; 299 } else { 300 /* 301 * The inode itself. 302 */ 303 cursor.key_beg.key = 0; 304 cursor.key_end.key = 0; 305 cursor.key_beg.rec_type = HAMMER_RECTYPE_INODE; 306 cursor.key_end.rec_type = HAMMER_RECTYPE_INODE; 307 cursor.key_beg.localization = ip->obj_localization + 308 HAMMER_LOCALIZE_INODE; 309 cursor.key_end.localization = ip->obj_localization + 310 HAMMER_LOCALIZE_INODE; 311 } 312 313 error = hammer_btree_first(&cursor); 314 while (error == 0) { 315 elm = &cursor.node->ondisk->elms[cursor.index]; 316 317 add_history(ip, hist, elm); 318 if (hist->head.flags & (HAMMER_IOC_HISTORY_NEXT_TID | 319 HAMMER_IOC_HISTORY_NEXT_KEY | 320 HAMMER_IOC_HISTORY_EOF)) { 321 break; 322 } 323 error = hammer_btree_iterate(&cursor); 324 } 325 if (error == ENOENT) { 326 hist->head.flags |= HAMMER_IOC_HISTORY_EOF; 327 error = 0; 328 } 329 hammer_done_cursor(&cursor); 330 return(error); 331 } 332 333 /* 334 * Add the scanned element to the ioctl return structure. Some special 335 * casing is required for regular files to accomodate how data ranges are 336 * stored on-disk. 337 */ 338 static void 339 add_history(hammer_inode_t ip, struct hammer_ioc_history *hist, 340 hammer_btree_elm_t elm) 341 { 342 int i; 343 344 if (elm->base.btype != HAMMER_BTREE_TYPE_RECORD) 345 return; 346 if ((hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) && 347 ip->ino_data.obj_type == HAMMER_OBJTYPE_REGFILE) { 348 /* 349 * Adjust nxt_key 350 */ 351 if (hist->nxt_key > elm->leaf.base.key - elm->leaf.data_len && 352 hist->key < elm->leaf.base.key - elm->leaf.data_len) { 353 hist->nxt_key = elm->leaf.base.key - elm->leaf.data_len; 354 } 355 if (hist->nxt_key > elm->leaf.base.key) 356 hist->nxt_key = elm->leaf.base.key; 357 358 /* 359 * Record is beyond MAXPHYS, there won't be any more records 360 * in the iteration covering the requested offset (key). 361 */ 362 if (elm->leaf.base.key >= MAXPHYS && 363 elm->leaf.base.key - MAXPHYS > hist->key) { 364 hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_KEY; 365 } 366 367 /* 368 * Data-range of record does not cover the key. 369 */ 370 if (elm->leaf.base.key - elm->leaf.data_len > hist->key) 371 return; 372 373 } else if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) { 374 /* 375 * Adjust nxt_key 376 */ 377 if (hist->nxt_key > elm->leaf.base.key && 378 hist->key < elm->leaf.base.key) { 379 hist->nxt_key = elm->leaf.base.key; 380 } 381 382 /* 383 * Record is beyond the requested key. 384 */ 385 if (elm->leaf.base.key > hist->key) 386 hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_KEY; 387 } 388 389 /* 390 * Add create_tid if it is in-bounds. 391 */ 392 i = hist->count; 393 if ((i == 0 || 394 elm->leaf.base.create_tid != hist->hist_ary[i - 1].tid) && 395 elm->leaf.base.create_tid >= hist->beg_tid && 396 elm->leaf.base.create_tid < hist->end_tid) { 397 if (hist->count == HAMMER_MAX_HISTORY_ELMS) { 398 hist->nxt_tid = elm->leaf.base.create_tid; 399 hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_TID; 400 return; 401 } 402 hist->hist_ary[i].tid = elm->leaf.base.create_tid; 403 hist->hist_ary[i].time32 = elm->leaf.create_ts; 404 ++hist->count; 405 } 406 407 /* 408 * Add delete_tid if it is in-bounds. Note that different portions 409 * of the history may have overlapping data ranges with different 410 * delete_tid's. If this case occurs the delete_tid may match the 411 * create_tid of a following record. XXX 412 * 413 * [ ] 414 * [ ] 415 */ 416 i = hist->count; 417 if (elm->leaf.base.delete_tid && 418 elm->leaf.base.delete_tid >= hist->beg_tid && 419 elm->leaf.base.delete_tid < hist->end_tid) { 420 if (i == HAMMER_MAX_HISTORY_ELMS) { 421 hist->nxt_tid = elm->leaf.base.delete_tid; 422 hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_TID; 423 return; 424 } 425 hist->hist_ary[i].tid = elm->leaf.base.delete_tid; 426 hist->hist_ary[i].time32 = elm->leaf.delete_ts; 427 ++hist->count; 428 } 429 } 430 431 /* 432 * Acquire synchronization TID 433 */ 434 static 435 int 436 hammer_ioc_synctid(hammer_transaction_t trans, hammer_inode_t ip, 437 struct hammer_ioc_synctid *std) 438 { 439 hammer_mount_t hmp = ip->hmp; 440 int error = 0; 441 442 switch(std->op) { 443 case HAMMER_SYNCTID_NONE: 444 std->tid = hmp->flusher.tid; /* inaccurate */ 445 break; 446 case HAMMER_SYNCTID_ASYNC: 447 hammer_queue_inodes_flusher(hmp, MNT_NOWAIT); 448 hammer_flusher_async(hmp, NULL); 449 std->tid = hmp->flusher.tid; /* inaccurate */ 450 break; 451 case HAMMER_SYNCTID_SYNC1: 452 hammer_queue_inodes_flusher(hmp, MNT_WAIT); 453 hammer_flusher_sync(hmp); 454 std->tid = hmp->flusher.tid; 455 break; 456 case HAMMER_SYNCTID_SYNC2: 457 hammer_queue_inodes_flusher(hmp, MNT_WAIT); 458 hammer_flusher_sync(hmp); 459 std->tid = hmp->flusher.tid; 460 hammer_flusher_sync(hmp); 461 break; 462 default: 463 error = EOPNOTSUPP; 464 break; 465 } 466 return(error); 467 } 468 469 /* 470 * Retrieve version info. 471 * 472 * Load min_version, wip_version, and max_versino. If cur_version is passed 473 * as 0 then load the current version into cur_version. Load the description 474 * for cur_version into the description array. 475 * 476 * Returns 0 on success, EINVAL if cur_version is non-zero and set to an 477 * unsupported value. 478 */ 479 static 480 int 481 hammer_ioc_get_version(hammer_transaction_t trans, hammer_inode_t ip, 482 struct hammer_ioc_version *ver) 483 { 484 int error = 0; 485 486 ver->min_version = HAMMER_VOL_VERSION_MIN; 487 ver->wip_version = HAMMER_VOL_VERSION_WIP; 488 ver->max_version = HAMMER_VOL_VERSION_MAX; 489 if (ver->cur_version == 0) 490 ver->cur_version = trans->hmp->version; 491 switch(ver->cur_version) { 492 case 1: 493 ksnprintf(ver->description, sizeof(ver->description), 494 "First HAMMER release (DragonFly 2.0+)"); 495 break; 496 case 2: 497 ksnprintf(ver->description, sizeof(ver->description), 498 "New directory entry layout (DragonFly 2.3+)"); 499 break; 500 case 3: 501 ksnprintf(ver->description, sizeof(ver->description), 502 "New snapshot management (DragonFly 2.5+)"); 503 break; 504 default: 505 ksnprintf(ver->description, sizeof(ver->description), 506 "Unknown"); 507 error = EINVAL; 508 break; 509 } 510 return(error); 511 }; 512 513 /* 514 * Set version info 515 */ 516 static 517 int 518 hammer_ioc_set_version(hammer_transaction_t trans, hammer_inode_t ip, 519 struct hammer_ioc_version *ver) 520 { 521 struct hammer_cursor cursor; 522 hammer_volume_t volume; 523 int error; 524 525 if (ver->cur_version < trans->hmp->version) 526 return(EINVAL); 527 if (ver->cur_version == trans->hmp->version) 528 return(0); 529 if (ver->cur_version > HAMMER_VOL_VERSION_MAX) 530 return(EINVAL); 531 if (trans->hmp->ronly) 532 return(EROFS); 533 534 /* 535 * Update the root volume header and the version cached in 536 * the hammer_mount structure. 537 */ 538 error = hammer_init_cursor(trans, &cursor, NULL, NULL); 539 if (error) 540 goto failed; 541 hammer_sync_lock_sh(trans); 542 543 volume = hammer_get_root_volume(cursor.trans->hmp, &error); 544 KKASSERT(error == 0); 545 hammer_modify_volume_field(cursor.trans, volume, vol_version); 546 volume->ondisk->vol_version = ver->cur_version; 547 cursor.trans->hmp->version = ver->cur_version; 548 hammer_modify_volume_done(volume); 549 hammer_rel_volume(volume, 0); 550 551 hammer_sync_unlock(trans); 552 failed: 553 ver->head.error = error; 554 hammer_done_cursor(&cursor); 555 return(0); 556 } 557 558 /* 559 * Get information 560 */ 561 static 562 int 563 hammer_ioc_get_info(hammer_transaction_t trans, struct hammer_ioc_info *info) { 564 565 struct hammer_volume_ondisk *od = trans->hmp->rootvol->ondisk; 566 struct hammer_mount *hm = trans->hmp; 567 568 /* Fill the structure with the necessary information */ 569 _hammer_checkspace(hm, HAMMER_CHKSPC_WRITE, &info->rsvbigblocks); 570 info->rsvbigblocks = info->rsvbigblocks >> HAMMER_LARGEBLOCK_BITS; 571 strlcpy(info->vol_name, od->vol_name, sizeof(od->vol_name)); 572 573 info->vol_fsid = hm->fsid; 574 info->vol_fstype = od->vol_fstype; 575 info->version = hm->version; 576 577 info->inodes = od->vol0_stat_inodes; 578 info->bigblocks = od->vol0_stat_bigblocks; 579 info->freebigblocks = od->vol0_stat_freebigblocks; 580 info->nvolumes = hm->nvolumes; 581 582 return 0; 583 } 584 585 /* 586 * Add a snapshot transction id(s) to the list of snapshots. 587 * 588 * NOTE: Records are created with an allocated TID. If a flush cycle 589 * is in progress the record may be synced in the current flush 590 * cycle and the volume header will reflect the allocation of the 591 * TID, but the synchronization point may not catch up to the 592 * TID until the next flush cycle. 593 */ 594 static 595 int 596 hammer_ioc_add_snapshot(hammer_transaction_t trans, hammer_inode_t ip, 597 struct hammer_ioc_snapshot *snap) 598 { 599 hammer_mount_t hmp = ip->hmp; 600 struct hammer_btree_leaf_elm leaf; 601 struct hammer_cursor cursor; 602 int error; 603 604 /* 605 * Validate structure 606 */ 607 if (snap->count > HAMMER_SNAPS_PER_IOCTL) 608 return (EINVAL); 609 if (snap->index > snap->count) 610 return (EINVAL); 611 612 hammer_lock_ex(&hmp->snapshot_lock); 613 again: 614 /* 615 * Look for keys starting after the previous iteration, or at 616 * the beginning if snap->count is 0. 617 */ 618 error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL); 619 if (error) { 620 hammer_done_cursor(&cursor); 621 return(error); 622 } 623 624 cursor.asof = HAMMER_MAX_TID; 625 cursor.flags |= HAMMER_CURSOR_BACKEND | HAMMER_CURSOR_ASOF; 626 627 bzero(&leaf, sizeof(leaf)); 628 leaf.base.obj_id = HAMMER_OBJID_ROOT; 629 leaf.base.rec_type = HAMMER_RECTYPE_SNAPSHOT; 630 leaf.base.create_tid = hammer_alloc_tid(hmp, 1); 631 leaf.base.btype = HAMMER_BTREE_TYPE_RECORD; 632 leaf.base.localization = ip->obj_localization + HAMMER_LOCALIZE_INODE; 633 leaf.data_len = sizeof(struct hammer_snapshot_data); 634 635 while (snap->index < snap->count) { 636 leaf.base.key = (int64_t)snap->snaps[snap->index].tid; 637 cursor.key_beg = leaf.base; 638 error = hammer_btree_lookup(&cursor); 639 if (error == 0) { 640 error = EEXIST; 641 break; 642 } 643 644 cursor.flags &= ~HAMMER_CURSOR_ASOF; 645 error = hammer_create_at_cursor(&cursor, &leaf, 646 &snap->snaps[snap->index], 647 HAMMER_CREATE_MODE_SYS); 648 if (error == EDEADLK) { 649 hammer_done_cursor(&cursor); 650 goto again; 651 } 652 cursor.flags |= HAMMER_CURSOR_ASOF; 653 if (error) 654 break; 655 ++snap->index; 656 } 657 snap->head.error = error; 658 hammer_done_cursor(&cursor); 659 hammer_unlock(&hmp->snapshot_lock); 660 return(0); 661 } 662 663 /* 664 * Delete snapshot transaction id(s) from the list of snapshots. 665 */ 666 static 667 int 668 hammer_ioc_del_snapshot(hammer_transaction_t trans, hammer_inode_t ip, 669 struct hammer_ioc_snapshot *snap) 670 { 671 hammer_mount_t hmp = ip->hmp; 672 struct hammer_cursor cursor; 673 int error; 674 675 /* 676 * Validate structure 677 */ 678 if (snap->count > HAMMER_SNAPS_PER_IOCTL) 679 return (EINVAL); 680 if (snap->index > snap->count) 681 return (EINVAL); 682 683 hammer_lock_ex(&hmp->snapshot_lock); 684 again: 685 /* 686 * Look for keys starting after the previous iteration, or at 687 * the beginning if snap->count is 0. 688 */ 689 error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL); 690 if (error) { 691 hammer_done_cursor(&cursor); 692 return(error); 693 } 694 695 cursor.key_beg.obj_id = HAMMER_OBJID_ROOT; 696 cursor.key_beg.create_tid = 0; 697 cursor.key_beg.delete_tid = 0; 698 cursor.key_beg.obj_type = 0; 699 cursor.key_beg.rec_type = HAMMER_RECTYPE_SNAPSHOT; 700 cursor.key_beg.localization = ip->obj_localization + HAMMER_LOCALIZE_INODE; 701 cursor.asof = HAMMER_MAX_TID; 702 cursor.flags |= HAMMER_CURSOR_ASOF; 703 704 while (snap->index < snap->count) { 705 cursor.key_beg.key = (int64_t)snap->snaps[snap->index].tid; 706 error = hammer_btree_lookup(&cursor); 707 if (error) 708 break; 709 error = hammer_btree_extract(&cursor, HAMMER_CURSOR_GET_LEAF); 710 if (error) 711 break; 712 error = hammer_delete_at_cursor(&cursor, HAMMER_DELETE_DESTROY, 713 0, 0, 0, NULL); 714 if (error == EDEADLK) { 715 hammer_done_cursor(&cursor); 716 goto again; 717 } 718 if (error) 719 break; 720 ++snap->index; 721 } 722 snap->head.error = error; 723 hammer_done_cursor(&cursor); 724 hammer_unlock(&hmp->snapshot_lock); 725 return(0); 726 } 727 728 /* 729 * Retrieve as many snapshot ids as possible or until the array is 730 * full, starting after the last transction id passed in. If count 731 * is 0 we retrieve starting at the beginning. 732 * 733 * NOTE: Because the b-tree key field is signed but transaction ids 734 * are unsigned the returned list will be signed-sorted instead 735 * of unsigned sorted. The Caller must still sort the aggregate 736 * results. 737 */ 738 static 739 int 740 hammer_ioc_get_snapshot(hammer_transaction_t trans, hammer_inode_t ip, 741 struct hammer_ioc_snapshot *snap) 742 { 743 struct hammer_cursor cursor; 744 int error; 745 746 /* 747 * Validate structure 748 */ 749 if (snap->index != 0) 750 return (EINVAL); 751 if (snap->count > HAMMER_SNAPS_PER_IOCTL) 752 return (EINVAL); 753 754 /* 755 * Look for keys starting after the previous iteration, or at 756 * the beginning if snap->count is 0. 757 */ 758 error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL); 759 if (error) { 760 hammer_done_cursor(&cursor); 761 return(error); 762 } 763 764 cursor.key_beg.obj_id = HAMMER_OBJID_ROOT; 765 cursor.key_beg.create_tid = 0; 766 cursor.key_beg.delete_tid = 0; 767 cursor.key_beg.obj_type = 0; 768 cursor.key_beg.rec_type = HAMMER_RECTYPE_SNAPSHOT; 769 cursor.key_beg.localization = ip->obj_localization + HAMMER_LOCALIZE_INODE; 770 if (snap->count == 0) 771 cursor.key_beg.key = HAMMER_MIN_KEY; 772 else 773 cursor.key_beg.key = (int64_t)snap->snaps[snap->count - 1].tid + 1; 774 775 cursor.key_end = cursor.key_beg; 776 cursor.key_end.key = HAMMER_MAX_KEY; 777 cursor.asof = HAMMER_MAX_TID; 778 cursor.flags |= HAMMER_CURSOR_END_EXCLUSIVE | HAMMER_CURSOR_ASOF; 779 780 snap->count = 0; 781 782 error = hammer_btree_first(&cursor); 783 while (error == 0 && snap->count < HAMMER_SNAPS_PER_IOCTL) { 784 error = hammer_btree_extract(&cursor, HAMMER_CURSOR_GET_LEAF); 785 if (error) 786 break; 787 if (cursor.leaf->base.rec_type == HAMMER_RECTYPE_SNAPSHOT) { 788 error = hammer_btree_extract(&cursor, HAMMER_CURSOR_GET_LEAF | 789 HAMMER_CURSOR_GET_DATA); 790 snap->snaps[snap->count] = cursor.data->snap; 791 ++snap->count; 792 } 793 error = hammer_btree_iterate(&cursor); 794 } 795 796 if (error == ENOENT) { 797 snap->head.flags |= HAMMER_IOC_SNAPSHOT_EOF; 798 error = 0; 799 } 800 snap->head.error = error; 801 hammer_done_cursor(&cursor); 802 return(0); 803 } 804 805 /* 806 * Retrieve the PFS hammer cleanup utility config record. This is 807 * different (newer than) the PFS config. 808 */ 809 static 810 int 811 hammer_ioc_get_config(hammer_transaction_t trans, hammer_inode_t ip, 812 struct hammer_ioc_config *config) 813 { 814 struct hammer_cursor cursor; 815 int error; 816 817 error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL); 818 if (error) { 819 hammer_done_cursor(&cursor); 820 return(error); 821 } 822 823 cursor.key_beg.obj_id = HAMMER_OBJID_ROOT; 824 cursor.key_beg.create_tid = 0; 825 cursor.key_beg.delete_tid = 0; 826 cursor.key_beg.obj_type = 0; 827 cursor.key_beg.rec_type = HAMMER_RECTYPE_CONFIG; 828 cursor.key_beg.localization = ip->obj_localization + HAMMER_LOCALIZE_INODE; 829 cursor.key_beg.key = 0; /* config space page 0 */ 830 831 cursor.asof = HAMMER_MAX_TID; 832 cursor.flags |= HAMMER_CURSOR_ASOF; 833 834 error = hammer_btree_lookup(&cursor); 835 if (error == 0) { 836 error = hammer_btree_extract(&cursor, HAMMER_CURSOR_GET_LEAF | 837 HAMMER_CURSOR_GET_DATA); 838 if (error == 0) 839 config->config = cursor.data->config; 840 } 841 /* error can be ENOENT */ 842 config->head.error = error; 843 hammer_done_cursor(&cursor); 844 return(0); 845 } 846 847 /* 848 * Retrieve the PFS hammer cleanup utility config record. This is 849 * different (newer than) the PFS config. 850 * 851 * This is kinda a hack. 852 */ 853 static 854 int 855 hammer_ioc_set_config(hammer_transaction_t trans, hammer_inode_t ip, 856 struct hammer_ioc_config *config) 857 { 858 struct hammer_btree_leaf_elm leaf; 859 struct hammer_cursor cursor; 860 hammer_mount_t hmp = ip->hmp; 861 int error; 862 863 again: 864 error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL); 865 if (error) { 866 hammer_done_cursor(&cursor); 867 return(error); 868 } 869 870 bzero(&leaf, sizeof(leaf)); 871 leaf.base.obj_id = HAMMER_OBJID_ROOT; 872 leaf.base.rec_type = HAMMER_RECTYPE_CONFIG; 873 leaf.base.create_tid = hammer_alloc_tid(hmp, 1); 874 leaf.base.btype = HAMMER_BTREE_TYPE_RECORD; 875 leaf.base.localization = ip->obj_localization + HAMMER_LOCALIZE_INODE; 876 leaf.base.key = 0; /* page 0 */ 877 leaf.data_len = sizeof(struct hammer_config_data); 878 879 cursor.key_beg = leaf.base; 880 881 cursor.asof = HAMMER_MAX_TID; 882 cursor.flags |= HAMMER_CURSOR_BACKEND | HAMMER_CURSOR_ASOF; 883 884 error = hammer_btree_lookup(&cursor); 885 if (error == 0) { 886 error = hammer_btree_extract(&cursor, HAMMER_CURSOR_GET_LEAF | 887 HAMMER_CURSOR_GET_DATA); 888 error = hammer_delete_at_cursor(&cursor, HAMMER_DELETE_DESTROY, 889 0, 0, 0, NULL); 890 if (error == EDEADLK) { 891 hammer_done_cursor(&cursor); 892 goto again; 893 } 894 } 895 if (error == ENOENT) 896 error = 0; 897 if (error == 0) { 898 cursor.flags &= ~HAMMER_CURSOR_ASOF; 899 cursor.key_beg = leaf.base; 900 error = hammer_create_at_cursor(&cursor, &leaf, 901 &config->config, 902 HAMMER_CREATE_MODE_SYS); 903 if (error == EDEADLK) { 904 hammer_done_cursor(&cursor); 905 goto again; 906 } 907 } 908 config->head.error = error; 909 hammer_done_cursor(&cursor); 910 return(0); 911 } 912