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 /* 92 * Rebalancing needs to lock a lot of B-Tree nodes. The 93 * children and children's children. Systems with very 94 * little memory will not be able to do it. 95 */ 96 if (error == 0 && nbuf < HAMMER_REBALANCE_MIN_BUFS) { 97 kprintf("hammer: System has insufficient buffers " 98 "to rebalance the tree. nbuf < %d\n", 99 HAMMER_REBALANCE_MIN_BUFS); 100 error = ENOSPC; 101 } 102 if (error == 0) { 103 error = hammer_ioc_rebalance(&trans, ip, 104 (struct hammer_ioc_rebalance *)data); 105 } 106 break; 107 case HAMMERIOC_SYNCTID: 108 error = hammer_ioc_synctid(&trans, ip, 109 (struct hammer_ioc_synctid *)data); 110 break; 111 case HAMMERIOC_GET_PSEUDOFS: 112 error = hammer_ioc_get_pseudofs(&trans, ip, 113 (struct hammer_ioc_pseudofs_rw *)data); 114 break; 115 case HAMMERIOC_SET_PSEUDOFS: 116 if (error == 0) { 117 error = hammer_ioc_set_pseudofs(&trans, ip, cred, 118 (struct hammer_ioc_pseudofs_rw *)data); 119 } 120 break; 121 case HAMMERIOC_UPG_PSEUDOFS: 122 if (error == 0) { 123 error = hammer_ioc_upgrade_pseudofs(&trans, ip, 124 (struct hammer_ioc_pseudofs_rw *)data); 125 } 126 break; 127 case HAMMERIOC_DGD_PSEUDOFS: 128 if (error == 0) { 129 error = hammer_ioc_downgrade_pseudofs(&trans, ip, 130 (struct hammer_ioc_pseudofs_rw *)data); 131 } 132 break; 133 case HAMMERIOC_RMR_PSEUDOFS: 134 if (error == 0) { 135 error = hammer_ioc_destroy_pseudofs(&trans, ip, 136 (struct hammer_ioc_pseudofs_rw *)data); 137 } 138 break; 139 case HAMMERIOC_WAI_PSEUDOFS: 140 if (error == 0) { 141 error = hammer_ioc_wait_pseudofs(&trans, ip, 142 (struct hammer_ioc_pseudofs_rw *)data); 143 } 144 break; 145 case HAMMERIOC_MIRROR_READ: 146 if (error == 0) { 147 error = hammer_ioc_mirror_read(&trans, ip, 148 (struct hammer_ioc_mirror_rw *)data); 149 } 150 break; 151 case HAMMERIOC_MIRROR_WRITE: 152 if (error == 0) { 153 error = hammer_ioc_mirror_write(&trans, ip, 154 (struct hammer_ioc_mirror_rw *)data); 155 } 156 break; 157 case HAMMERIOC_GET_VERSION: 158 error = hammer_ioc_get_version(&trans, ip, 159 (struct hammer_ioc_version *)data); 160 break; 161 case HAMMERIOC_GET_INFO: 162 error = hammer_ioc_get_info(&trans, 163 (struct hammer_ioc_info *)data); 164 break; 165 case HAMMERIOC_SET_VERSION: 166 if (error == 0) { 167 error = hammer_ioc_set_version(&trans, ip, 168 (struct hammer_ioc_version *)data); 169 } 170 break; 171 case HAMMERIOC_ADD_VOLUME: 172 if (error == 0) { 173 error = priv_check_cred(cred, PRIV_HAMMER_VOLUME, 0); 174 if (error == 0) 175 error = hammer_ioc_volume_add(&trans, ip, 176 (struct hammer_ioc_volume *)data); 177 } 178 break; 179 case HAMMERIOC_DEL_VOLUME: 180 if (error == 0) { 181 error = priv_check_cred(cred, PRIV_HAMMER_VOLUME, 0); 182 if (error == 0) 183 error = hammer_ioc_volume_del(&trans, ip, 184 (struct hammer_ioc_volume *)data); 185 } 186 break; 187 case HAMMERIOC_ADD_SNAPSHOT: 188 if (error == 0) { 189 error = hammer_ioc_add_snapshot( 190 &trans, ip, (struct hammer_ioc_snapshot *)data); 191 } 192 break; 193 case HAMMERIOC_DEL_SNAPSHOT: 194 if (error == 0) { 195 error = hammer_ioc_del_snapshot( 196 &trans, ip, (struct hammer_ioc_snapshot *)data); 197 } 198 break; 199 case HAMMERIOC_GET_SNAPSHOT: 200 error = hammer_ioc_get_snapshot( 201 &trans, ip, (struct hammer_ioc_snapshot *)data); 202 break; 203 case HAMMERIOC_GET_CONFIG: 204 error = hammer_ioc_get_config( 205 &trans, ip, (struct hammer_ioc_config *)data); 206 break; 207 case HAMMERIOC_SET_CONFIG: 208 if (error == 0) { 209 error = hammer_ioc_set_config( 210 &trans, ip, (struct hammer_ioc_config *)data); 211 } 212 break; 213 default: 214 error = EOPNOTSUPP; 215 break; 216 } 217 hammer_done_transaction(&trans); 218 return (error); 219 } 220 221 /* 222 * Iterate through an object's inode or an object's records and record 223 * modification TIDs. 224 */ 225 static void add_history(hammer_inode_t ip, struct hammer_ioc_history *hist, 226 hammer_btree_elm_t elm); 227 228 static 229 int 230 hammer_ioc_gethistory(hammer_transaction_t trans, hammer_inode_t ip, 231 struct hammer_ioc_history *hist) 232 { 233 struct hammer_cursor cursor; 234 hammer_btree_elm_t elm; 235 int error; 236 237 /* 238 * Validate the structure and initialize for return. 239 */ 240 if (hist->beg_tid > hist->end_tid) 241 return(EINVAL); 242 if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) { 243 if (hist->key > hist->nxt_key) 244 return(EINVAL); 245 } 246 247 hist->obj_id = ip->obj_id; 248 hist->count = 0; 249 hist->nxt_tid = hist->end_tid; 250 hist->head.flags &= ~HAMMER_IOC_HISTORY_NEXT_TID; 251 hist->head.flags &= ~HAMMER_IOC_HISTORY_NEXT_KEY; 252 hist->head.flags &= ~HAMMER_IOC_HISTORY_EOF; 253 hist->head.flags &= ~HAMMER_IOC_HISTORY_UNSYNCED; 254 if ((ip->flags & HAMMER_INODE_MODMASK) & 255 ~(HAMMER_INODE_ATIME | HAMMER_INODE_MTIME)) { 256 hist->head.flags |= HAMMER_IOC_HISTORY_UNSYNCED; 257 } 258 259 /* 260 * Setup the cursor. We can't handle undeletable records 261 * (create_tid of 0) at the moment. A create_tid of 0 has 262 * a special meaning and cannot be specified in the cursor. 263 */ 264 error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL); 265 if (error) { 266 hammer_done_cursor(&cursor); 267 return(error); 268 } 269 270 cursor.key_beg.obj_id = hist->obj_id; 271 cursor.key_beg.create_tid = hist->beg_tid; 272 cursor.key_beg.delete_tid = 0; 273 cursor.key_beg.obj_type = 0; 274 if (cursor.key_beg.create_tid == HAMMER_MIN_TID) 275 cursor.key_beg.create_tid = 1; 276 277 cursor.key_end.obj_id = hist->obj_id; 278 cursor.key_end.create_tid = hist->end_tid; 279 cursor.key_end.delete_tid = 0; 280 cursor.key_end.obj_type = 0; 281 282 cursor.flags |= HAMMER_CURSOR_END_EXCLUSIVE; 283 284 if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) { 285 /* 286 * key-range within the file. For a regular file the 287 * on-disk key represents BASE+LEN, not BASE, so the 288 * first possible record containing the offset 'key' 289 * has an on-disk key of (key + 1). 290 */ 291 cursor.key_beg.key = hist->key; 292 cursor.key_end.key = HAMMER_MAX_KEY; 293 cursor.key_beg.localization = ip->obj_localization + 294 HAMMER_LOCALIZE_MISC; 295 cursor.key_end.localization = ip->obj_localization + 296 HAMMER_LOCALIZE_MISC; 297 298 switch(ip->ino_data.obj_type) { 299 case HAMMER_OBJTYPE_REGFILE: 300 ++cursor.key_beg.key; 301 cursor.key_beg.rec_type = HAMMER_RECTYPE_DATA; 302 break; 303 case HAMMER_OBJTYPE_DIRECTORY: 304 cursor.key_beg.rec_type = HAMMER_RECTYPE_DIRENTRY; 305 cursor.key_beg.localization = ip->obj_localization + 306 hammer_dir_localization(ip); 307 cursor.key_end.localization = ip->obj_localization + 308 hammer_dir_localization(ip); 309 break; 310 case HAMMER_OBJTYPE_DBFILE: 311 cursor.key_beg.rec_type = HAMMER_RECTYPE_DB; 312 break; 313 default: 314 error = EINVAL; 315 break; 316 } 317 cursor.key_end.rec_type = cursor.key_beg.rec_type; 318 } else { 319 /* 320 * The inode itself. 321 */ 322 cursor.key_beg.key = 0; 323 cursor.key_end.key = 0; 324 cursor.key_beg.rec_type = HAMMER_RECTYPE_INODE; 325 cursor.key_end.rec_type = HAMMER_RECTYPE_INODE; 326 cursor.key_beg.localization = ip->obj_localization + 327 HAMMER_LOCALIZE_INODE; 328 cursor.key_end.localization = ip->obj_localization + 329 HAMMER_LOCALIZE_INODE; 330 } 331 332 error = hammer_btree_first(&cursor); 333 while (error == 0) { 334 elm = &cursor.node->ondisk->elms[cursor.index]; 335 336 add_history(ip, hist, elm); 337 if (hist->head.flags & (HAMMER_IOC_HISTORY_NEXT_TID | 338 HAMMER_IOC_HISTORY_NEXT_KEY | 339 HAMMER_IOC_HISTORY_EOF)) { 340 break; 341 } 342 error = hammer_btree_iterate(&cursor); 343 } 344 if (error == ENOENT) { 345 hist->head.flags |= HAMMER_IOC_HISTORY_EOF; 346 error = 0; 347 } 348 hammer_done_cursor(&cursor); 349 return(error); 350 } 351 352 /* 353 * Add the scanned element to the ioctl return structure. Some special 354 * casing is required for regular files to accomodate how data ranges are 355 * stored on-disk. 356 */ 357 static void 358 add_history(hammer_inode_t ip, struct hammer_ioc_history *hist, 359 hammer_btree_elm_t elm) 360 { 361 int i; 362 363 if (elm->base.btype != HAMMER_BTREE_TYPE_RECORD) 364 return; 365 if ((hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) && 366 ip->ino_data.obj_type == HAMMER_OBJTYPE_REGFILE) { 367 /* 368 * Adjust nxt_key 369 */ 370 if (hist->nxt_key > elm->leaf.base.key - elm->leaf.data_len && 371 hist->key < elm->leaf.base.key - elm->leaf.data_len) { 372 hist->nxt_key = elm->leaf.base.key - elm->leaf.data_len; 373 } 374 if (hist->nxt_key > elm->leaf.base.key) 375 hist->nxt_key = elm->leaf.base.key; 376 377 /* 378 * Record is beyond MAXPHYS, there won't be any more records 379 * in the iteration covering the requested offset (key). 380 */ 381 if (elm->leaf.base.key >= MAXPHYS && 382 elm->leaf.base.key - MAXPHYS > hist->key) { 383 hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_KEY; 384 } 385 386 /* 387 * Data-range of record does not cover the key. 388 */ 389 if (elm->leaf.base.key - elm->leaf.data_len > hist->key) 390 return; 391 392 } else if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) { 393 /* 394 * Adjust nxt_key 395 */ 396 if (hist->nxt_key > elm->leaf.base.key && 397 hist->key < elm->leaf.base.key) { 398 hist->nxt_key = elm->leaf.base.key; 399 } 400 401 /* 402 * Record is beyond the requested key. 403 */ 404 if (elm->leaf.base.key > hist->key) 405 hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_KEY; 406 } 407 408 /* 409 * Add create_tid if it is in-bounds. 410 */ 411 i = hist->count; 412 if ((i == 0 || 413 elm->leaf.base.create_tid != hist->hist_ary[i - 1].tid) && 414 elm->leaf.base.create_tid >= hist->beg_tid && 415 elm->leaf.base.create_tid < hist->end_tid) { 416 if (hist->count == HAMMER_MAX_HISTORY_ELMS) { 417 hist->nxt_tid = elm->leaf.base.create_tid; 418 hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_TID; 419 return; 420 } 421 hist->hist_ary[i].tid = elm->leaf.base.create_tid; 422 hist->hist_ary[i].time32 = elm->leaf.create_ts; 423 ++hist->count; 424 } 425 426 /* 427 * Add delete_tid if it is in-bounds. Note that different portions 428 * of the history may have overlapping data ranges with different 429 * delete_tid's. If this case occurs the delete_tid may match the 430 * create_tid of a following record. XXX 431 * 432 * [ ] 433 * [ ] 434 */ 435 i = hist->count; 436 if (elm->leaf.base.delete_tid && 437 elm->leaf.base.delete_tid >= hist->beg_tid && 438 elm->leaf.base.delete_tid < hist->end_tid) { 439 if (i == HAMMER_MAX_HISTORY_ELMS) { 440 hist->nxt_tid = elm->leaf.base.delete_tid; 441 hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_TID; 442 return; 443 } 444 hist->hist_ary[i].tid = elm->leaf.base.delete_tid; 445 hist->hist_ary[i].time32 = elm->leaf.delete_ts; 446 ++hist->count; 447 } 448 } 449 450 /* 451 * Acquire synchronization TID 452 */ 453 static 454 int 455 hammer_ioc_synctid(hammer_transaction_t trans, hammer_inode_t ip, 456 struct hammer_ioc_synctid *std) 457 { 458 hammer_mount_t hmp = ip->hmp; 459 int error = 0; 460 461 switch(std->op) { 462 case HAMMER_SYNCTID_NONE: 463 std->tid = hmp->flusher.tid; /* inaccurate */ 464 break; 465 case HAMMER_SYNCTID_ASYNC: 466 hammer_queue_inodes_flusher(hmp, MNT_NOWAIT); 467 hammer_flusher_async(hmp, NULL); 468 std->tid = hmp->flusher.tid; /* inaccurate */ 469 break; 470 case HAMMER_SYNCTID_SYNC1: 471 hammer_queue_inodes_flusher(hmp, MNT_WAIT); 472 hammer_flusher_sync(hmp); 473 std->tid = hmp->flusher.tid; 474 break; 475 case HAMMER_SYNCTID_SYNC2: 476 hammer_queue_inodes_flusher(hmp, MNT_WAIT); 477 hammer_flusher_sync(hmp); 478 std->tid = hmp->flusher.tid; 479 hammer_flusher_sync(hmp); 480 break; 481 default: 482 error = EOPNOTSUPP; 483 break; 484 } 485 return(error); 486 } 487 488 /* 489 * Retrieve version info. 490 * 491 * Load min_version, wip_version, and max_versino. If cur_version is passed 492 * as 0 then load the current version into cur_version. Load the description 493 * for cur_version into the description array. 494 * 495 * Returns 0 on success, EINVAL if cur_version is non-zero and set to an 496 * unsupported value. 497 */ 498 static 499 int 500 hammer_ioc_get_version(hammer_transaction_t trans, hammer_inode_t ip, 501 struct hammer_ioc_version *ver) 502 { 503 int error = 0; 504 505 ver->min_version = HAMMER_VOL_VERSION_MIN; 506 ver->wip_version = HAMMER_VOL_VERSION_WIP; 507 ver->max_version = HAMMER_VOL_VERSION_MAX; 508 if (ver->cur_version == 0) 509 ver->cur_version = trans->hmp->version; 510 switch(ver->cur_version) { 511 case 1: 512 ksnprintf(ver->description, sizeof(ver->description), 513 "First HAMMER release (DragonFly 2.0+)"); 514 break; 515 case 2: 516 ksnprintf(ver->description, sizeof(ver->description), 517 "New directory entry layout (DragonFly 2.3+)"); 518 break; 519 case 3: 520 ksnprintf(ver->description, sizeof(ver->description), 521 "New snapshot management (DragonFly 2.5+)"); 522 break; 523 case 4: 524 ksnprintf(ver->description, sizeof(ver->description), 525 "New undo/flush, faster flush/sync (DragonFly 2.5+)"); 526 break; 527 default: 528 ksnprintf(ver->description, sizeof(ver->description), 529 "Unknown"); 530 error = EINVAL; 531 break; 532 } 533 return(error); 534 }; 535 536 /* 537 * Set version info 538 */ 539 static 540 int 541 hammer_ioc_set_version(hammer_transaction_t trans, hammer_inode_t ip, 542 struct hammer_ioc_version *ver) 543 { 544 hammer_mount_t hmp = trans->hmp; 545 struct hammer_cursor cursor; 546 hammer_volume_t volume; 547 int error; 548 int over = hmp->version; 549 550 /* 551 * Generally do not allow downgrades. However, version 4 can 552 * be downgraded to version 3. 553 */ 554 if (ver->cur_version < hmp->version) { 555 if (!(ver->cur_version == 3 && hmp->version == 4)) 556 return(EINVAL); 557 } 558 if (ver->cur_version == hmp->version) 559 return(0); 560 if (ver->cur_version > HAMMER_VOL_VERSION_MAX) 561 return(EINVAL); 562 if (hmp->ronly) 563 return(EROFS); 564 565 /* 566 * Update the root volume header and the version cached in 567 * the hammer_mount structure. 568 */ 569 error = hammer_init_cursor(trans, &cursor, NULL, NULL); 570 if (error) 571 goto failed; 572 hammer_lock_ex(&hmp->flusher.finalize_lock); 573 hammer_sync_lock_ex(trans); 574 hmp->version = ver->cur_version; 575 576 /* 577 * If upgrading from version < 4 to version >= 4 the UNDO FIFO 578 * must be reinitialized. 579 */ 580 if (over < HAMMER_VOL_VERSION_FOUR && 581 ver->cur_version >= HAMMER_VOL_VERSION_FOUR) { 582 kprintf("upgrade undo to version 4\n"); 583 error = hammer_upgrade_undo_4(trans); 584 if (error) 585 goto failed; 586 } 587 588 /* 589 * Adjust the version in the volume header 590 */ 591 volume = hammer_get_root_volume(hmp, &error); 592 KKASSERT(error == 0); 593 hammer_modify_volume_field(cursor.trans, volume, vol_version); 594 volume->ondisk->vol_version = ver->cur_version; 595 hammer_modify_volume_done(volume); 596 hammer_rel_volume(volume, 0); 597 598 hammer_sync_unlock(trans); 599 hammer_unlock(&hmp->flusher.finalize_lock); 600 failed: 601 ver->head.error = error; 602 hammer_done_cursor(&cursor); 603 return(0); 604 } 605 606 /* 607 * Get information 608 */ 609 static 610 int 611 hammer_ioc_get_info(hammer_transaction_t trans, struct hammer_ioc_info *info) { 612 613 struct hammer_volume_ondisk *od = trans->hmp->rootvol->ondisk; 614 struct hammer_mount *hm = trans->hmp; 615 616 /* Fill the structure with the necessary information */ 617 _hammer_checkspace(hm, HAMMER_CHKSPC_WRITE, &info->rsvbigblocks); 618 info->rsvbigblocks = info->rsvbigblocks >> HAMMER_LARGEBLOCK_BITS; 619 strlcpy(info->vol_name, od->vol_name, sizeof(od->vol_name)); 620 621 info->vol_fsid = hm->fsid; 622 info->vol_fstype = od->vol_fstype; 623 info->version = hm->version; 624 625 info->inodes = od->vol0_stat_inodes; 626 info->bigblocks = od->vol0_stat_bigblocks; 627 info->freebigblocks = od->vol0_stat_freebigblocks; 628 info->nvolumes = hm->nvolumes; 629 630 return 0; 631 } 632 633 /* 634 * Add a snapshot transction id(s) to the list of snapshots. 635 * 636 * NOTE: Records are created with an allocated TID. If a flush cycle 637 * is in progress the record may be synced in the current flush 638 * cycle and the volume header will reflect the allocation of the 639 * TID, but the synchronization point may not catch up to the 640 * TID until the next flush cycle. 641 */ 642 static 643 int 644 hammer_ioc_add_snapshot(hammer_transaction_t trans, hammer_inode_t ip, 645 struct hammer_ioc_snapshot *snap) 646 { 647 hammer_mount_t hmp = ip->hmp; 648 struct hammer_btree_leaf_elm leaf; 649 struct hammer_cursor cursor; 650 int error; 651 652 /* 653 * Validate structure 654 */ 655 if (snap->count > HAMMER_SNAPS_PER_IOCTL) 656 return (EINVAL); 657 if (snap->index > snap->count) 658 return (EINVAL); 659 660 hammer_lock_ex(&hmp->snapshot_lock); 661 again: 662 /* 663 * Look for keys starting after the previous iteration, or at 664 * the beginning if snap->count is 0. 665 */ 666 error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL); 667 if (error) { 668 hammer_done_cursor(&cursor); 669 return(error); 670 } 671 672 cursor.asof = HAMMER_MAX_TID; 673 cursor.flags |= HAMMER_CURSOR_BACKEND | HAMMER_CURSOR_ASOF; 674 675 bzero(&leaf, sizeof(leaf)); 676 leaf.base.obj_id = HAMMER_OBJID_ROOT; 677 leaf.base.rec_type = HAMMER_RECTYPE_SNAPSHOT; 678 leaf.base.create_tid = hammer_alloc_tid(hmp, 1); 679 leaf.base.btype = HAMMER_BTREE_TYPE_RECORD; 680 leaf.base.localization = ip->obj_localization + HAMMER_LOCALIZE_INODE; 681 leaf.data_len = sizeof(struct hammer_snapshot_data); 682 683 while (snap->index < snap->count) { 684 leaf.base.key = (int64_t)snap->snaps[snap->index].tid; 685 cursor.key_beg = leaf.base; 686 error = hammer_btree_lookup(&cursor); 687 if (error == 0) { 688 error = EEXIST; 689 break; 690 } 691 692 /* 693 * NOTE: Must reload key_beg after an ASOF search because 694 * the create_tid may have been modified during the 695 * search. 696 */ 697 cursor.flags &= ~HAMMER_CURSOR_ASOF; 698 cursor.key_beg = leaf.base; 699 error = hammer_create_at_cursor(&cursor, &leaf, 700 &snap->snaps[snap->index], 701 HAMMER_CREATE_MODE_SYS); 702 if (error == EDEADLK) { 703 hammer_done_cursor(&cursor); 704 goto again; 705 } 706 cursor.flags |= HAMMER_CURSOR_ASOF; 707 if (error) 708 break; 709 ++snap->index; 710 } 711 snap->head.error = error; 712 hammer_done_cursor(&cursor); 713 hammer_unlock(&hmp->snapshot_lock); 714 return(0); 715 } 716 717 /* 718 * Delete snapshot transaction id(s) from the list of snapshots. 719 */ 720 static 721 int 722 hammer_ioc_del_snapshot(hammer_transaction_t trans, hammer_inode_t ip, 723 struct hammer_ioc_snapshot *snap) 724 { 725 hammer_mount_t hmp = ip->hmp; 726 struct hammer_cursor cursor; 727 int error; 728 729 /* 730 * Validate structure 731 */ 732 if (snap->count > HAMMER_SNAPS_PER_IOCTL) 733 return (EINVAL); 734 if (snap->index > snap->count) 735 return (EINVAL); 736 737 hammer_lock_ex(&hmp->snapshot_lock); 738 again: 739 /* 740 * Look for keys starting after the previous iteration, or at 741 * the beginning if snap->count is 0. 742 */ 743 error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL); 744 if (error) { 745 hammer_done_cursor(&cursor); 746 return(error); 747 } 748 749 cursor.key_beg.obj_id = HAMMER_OBJID_ROOT; 750 cursor.key_beg.create_tid = 0; 751 cursor.key_beg.delete_tid = 0; 752 cursor.key_beg.obj_type = 0; 753 cursor.key_beg.rec_type = HAMMER_RECTYPE_SNAPSHOT; 754 cursor.key_beg.localization = ip->obj_localization + HAMMER_LOCALIZE_INODE; 755 cursor.asof = HAMMER_MAX_TID; 756 cursor.flags |= HAMMER_CURSOR_ASOF; 757 758 while (snap->index < snap->count) { 759 cursor.key_beg.key = (int64_t)snap->snaps[snap->index].tid; 760 error = hammer_btree_lookup(&cursor); 761 if (error) 762 break; 763 error = hammer_btree_extract(&cursor, HAMMER_CURSOR_GET_LEAF); 764 if (error) 765 break; 766 error = hammer_delete_at_cursor(&cursor, HAMMER_DELETE_DESTROY, 767 0, 0, 0, NULL); 768 if (error == EDEADLK) { 769 hammer_done_cursor(&cursor); 770 goto again; 771 } 772 if (error) 773 break; 774 ++snap->index; 775 } 776 snap->head.error = error; 777 hammer_done_cursor(&cursor); 778 hammer_unlock(&hmp->snapshot_lock); 779 return(0); 780 } 781 782 /* 783 * Retrieve as many snapshot ids as possible or until the array is 784 * full, starting after the last transction id passed in. If count 785 * is 0 we retrieve starting at the beginning. 786 * 787 * NOTE: Because the b-tree key field is signed but transaction ids 788 * are unsigned the returned list will be signed-sorted instead 789 * of unsigned sorted. The Caller must still sort the aggregate 790 * results. 791 */ 792 static 793 int 794 hammer_ioc_get_snapshot(hammer_transaction_t trans, hammer_inode_t ip, 795 struct hammer_ioc_snapshot *snap) 796 { 797 struct hammer_cursor cursor; 798 int error; 799 800 /* 801 * Validate structure 802 */ 803 if (snap->index != 0) 804 return (EINVAL); 805 if (snap->count > HAMMER_SNAPS_PER_IOCTL) 806 return (EINVAL); 807 808 /* 809 * Look for keys starting after the previous iteration, or at 810 * the beginning if snap->count is 0. 811 */ 812 error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL); 813 if (error) { 814 hammer_done_cursor(&cursor); 815 return(error); 816 } 817 818 cursor.key_beg.obj_id = HAMMER_OBJID_ROOT; 819 cursor.key_beg.create_tid = 0; 820 cursor.key_beg.delete_tid = 0; 821 cursor.key_beg.obj_type = 0; 822 cursor.key_beg.rec_type = HAMMER_RECTYPE_SNAPSHOT; 823 cursor.key_beg.localization = ip->obj_localization + HAMMER_LOCALIZE_INODE; 824 if (snap->count == 0) 825 cursor.key_beg.key = HAMMER_MIN_KEY; 826 else 827 cursor.key_beg.key = (int64_t)snap->snaps[snap->count - 1].tid + 1; 828 829 cursor.key_end = cursor.key_beg; 830 cursor.key_end.key = HAMMER_MAX_KEY; 831 cursor.asof = HAMMER_MAX_TID; 832 cursor.flags |= HAMMER_CURSOR_END_EXCLUSIVE | HAMMER_CURSOR_ASOF; 833 834 snap->count = 0; 835 836 error = hammer_btree_first(&cursor); 837 while (error == 0 && snap->count < HAMMER_SNAPS_PER_IOCTL) { 838 error = hammer_btree_extract(&cursor, HAMMER_CURSOR_GET_LEAF); 839 if (error) 840 break; 841 if (cursor.leaf->base.rec_type == HAMMER_RECTYPE_SNAPSHOT) { 842 error = hammer_btree_extract( 843 &cursor, HAMMER_CURSOR_GET_LEAF | 844 HAMMER_CURSOR_GET_DATA); 845 snap->snaps[snap->count] = cursor.data->snap; 846 847 /* 848 * The snap data tid should match the key but might 849 * not due to a bug in the HAMMER v3 conversion code. 850 * 851 * This error will work itself out over time but we 852 * have to force a match or the snapshot will not 853 * be deletable. 854 */ 855 if (cursor.data->snap.tid != 856 (hammer_tid_t)cursor.leaf->base.key) { 857 kprintf("HAMMER: lo=%08x snapshot key " 858 "0x%016jx data mismatch 0x%016jx\n", 859 cursor.key_beg.localization, 860 (uintmax_t)cursor.data->snap.tid, 861 cursor.leaf->base.key); 862 kprintf("HAMMER: Probably left over from the " 863 "original v3 conversion, hammer " 864 "cleanup should get it eventually\n"); 865 snap->snaps[snap->count].tid = 866 cursor.leaf->base.key; 867 } 868 ++snap->count; 869 } 870 error = hammer_btree_iterate(&cursor); 871 } 872 873 if (error == ENOENT) { 874 snap->head.flags |= HAMMER_IOC_SNAPSHOT_EOF; 875 error = 0; 876 } 877 snap->head.error = error; 878 hammer_done_cursor(&cursor); 879 return(0); 880 } 881 882 /* 883 * Retrieve the PFS hammer cleanup utility config record. This is 884 * different (newer than) the PFS config. 885 */ 886 static 887 int 888 hammer_ioc_get_config(hammer_transaction_t trans, hammer_inode_t ip, 889 struct hammer_ioc_config *config) 890 { 891 struct hammer_cursor cursor; 892 int error; 893 894 error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL); 895 if (error) { 896 hammer_done_cursor(&cursor); 897 return(error); 898 } 899 900 cursor.key_beg.obj_id = HAMMER_OBJID_ROOT; 901 cursor.key_beg.create_tid = 0; 902 cursor.key_beg.delete_tid = 0; 903 cursor.key_beg.obj_type = 0; 904 cursor.key_beg.rec_type = HAMMER_RECTYPE_CONFIG; 905 cursor.key_beg.localization = ip->obj_localization + HAMMER_LOCALIZE_INODE; 906 cursor.key_beg.key = 0; /* config space page 0 */ 907 908 cursor.asof = HAMMER_MAX_TID; 909 cursor.flags |= HAMMER_CURSOR_ASOF; 910 911 error = hammer_btree_lookup(&cursor); 912 if (error == 0) { 913 error = hammer_btree_extract(&cursor, HAMMER_CURSOR_GET_LEAF | 914 HAMMER_CURSOR_GET_DATA); 915 if (error == 0) 916 config->config = cursor.data->config; 917 } 918 /* error can be ENOENT */ 919 config->head.error = error; 920 hammer_done_cursor(&cursor); 921 return(0); 922 } 923 924 /* 925 * Retrieve the PFS hammer cleanup utility config record. This is 926 * different (newer than) the PFS config. 927 * 928 * This is kinda a hack. 929 */ 930 static 931 int 932 hammer_ioc_set_config(hammer_transaction_t trans, hammer_inode_t ip, 933 struct hammer_ioc_config *config) 934 { 935 struct hammer_btree_leaf_elm leaf; 936 struct hammer_cursor cursor; 937 hammer_mount_t hmp = ip->hmp; 938 int error; 939 940 again: 941 error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL); 942 if (error) { 943 hammer_done_cursor(&cursor); 944 return(error); 945 } 946 947 bzero(&leaf, sizeof(leaf)); 948 leaf.base.obj_id = HAMMER_OBJID_ROOT; 949 leaf.base.rec_type = HAMMER_RECTYPE_CONFIG; 950 leaf.base.create_tid = hammer_alloc_tid(hmp, 1); 951 leaf.base.btype = HAMMER_BTREE_TYPE_RECORD; 952 leaf.base.localization = ip->obj_localization + HAMMER_LOCALIZE_INODE; 953 leaf.base.key = 0; /* page 0 */ 954 leaf.data_len = sizeof(struct hammer_config_data); 955 956 cursor.key_beg = leaf.base; 957 958 cursor.asof = HAMMER_MAX_TID; 959 cursor.flags |= HAMMER_CURSOR_BACKEND | HAMMER_CURSOR_ASOF; 960 961 error = hammer_btree_lookup(&cursor); 962 if (error == 0) { 963 error = hammer_btree_extract(&cursor, HAMMER_CURSOR_GET_LEAF | 964 HAMMER_CURSOR_GET_DATA); 965 error = hammer_delete_at_cursor(&cursor, HAMMER_DELETE_DESTROY, 966 0, 0, 0, NULL); 967 if (error == EDEADLK) { 968 hammer_done_cursor(&cursor); 969 goto again; 970 } 971 } 972 if (error == ENOENT) 973 error = 0; 974 if (error == 0) { 975 /* 976 * NOTE: Must reload key_beg after an ASOF search because 977 * the create_tid may have been modified during the 978 * search. 979 */ 980 cursor.flags &= ~HAMMER_CURSOR_ASOF; 981 cursor.key_beg = leaf.base; 982 error = hammer_create_at_cursor(&cursor, &leaf, 983 &config->config, 984 HAMMER_CREATE_MODE_SYS); 985 if (error == EDEADLK) { 986 hammer_done_cursor(&cursor); 987 goto again; 988 } 989 } 990 config->head.error = error; 991 hammer_done_cursor(&cursor); 992 return(0); 993 } 994