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 52 53 54 int 55 hammer_ioctl(hammer_inode_t ip, u_long com, caddr_t data, int fflag, 56 struct ucred *cred) 57 { 58 struct hammer_transaction trans; 59 int error; 60 61 error = priv_check_cred(cred, PRIV_HAMMER_IOCTL, 0); 62 63 hammer_start_transaction(&trans, ip->hmp); 64 65 switch(com) { 66 case HAMMERIOC_PRUNE: 67 if (error == 0) { 68 error = hammer_ioc_prune(&trans, ip, 69 (struct hammer_ioc_prune *)data); 70 } 71 break; 72 case HAMMERIOC_GETHISTORY: 73 error = hammer_ioc_gethistory(&trans, ip, 74 (struct hammer_ioc_history *)data); 75 break; 76 case HAMMERIOC_REBLOCK: 77 if (error == 0) { 78 error = hammer_ioc_reblock(&trans, ip, 79 (struct hammer_ioc_reblock *)data); 80 } 81 break; 82 case HAMMERIOC_REBALANCE: 83 if (error == 0) { 84 error = hammer_ioc_rebalance(&trans, ip, 85 (struct hammer_ioc_rebalance *)data); 86 } 87 break; 88 case HAMMERIOC_SYNCTID: 89 error = hammer_ioc_synctid(&trans, ip, 90 (struct hammer_ioc_synctid *)data); 91 break; 92 case HAMMERIOC_GET_PSEUDOFS: 93 error = hammer_ioc_get_pseudofs(&trans, ip, 94 (struct hammer_ioc_pseudofs_rw *)data); 95 break; 96 case HAMMERIOC_SET_PSEUDOFS: 97 if (error == 0) { 98 error = hammer_ioc_set_pseudofs(&trans, ip, cred, 99 (struct hammer_ioc_pseudofs_rw *)data); 100 } 101 break; 102 case HAMMERIOC_UPG_PSEUDOFS: 103 if (error == 0) { 104 error = hammer_ioc_upgrade_pseudofs(&trans, ip, 105 (struct hammer_ioc_pseudofs_rw *)data); 106 } 107 break; 108 case HAMMERIOC_DGD_PSEUDOFS: 109 if (error == 0) { 110 error = hammer_ioc_downgrade_pseudofs(&trans, ip, 111 (struct hammer_ioc_pseudofs_rw *)data); 112 } 113 break; 114 case HAMMERIOC_RMR_PSEUDOFS: 115 if (error == 0) { 116 error = hammer_ioc_destroy_pseudofs(&trans, ip, 117 (struct hammer_ioc_pseudofs_rw *)data); 118 } 119 break; 120 case HAMMERIOC_WAI_PSEUDOFS: 121 if (error == 0) { 122 error = hammer_ioc_wait_pseudofs(&trans, ip, 123 (struct hammer_ioc_pseudofs_rw *)data); 124 } 125 break; 126 case HAMMERIOC_MIRROR_READ: 127 if (error == 0) { 128 error = hammer_ioc_mirror_read(&trans, ip, 129 (struct hammer_ioc_mirror_rw *)data); 130 } 131 break; 132 case HAMMERIOC_MIRROR_WRITE: 133 if (error == 0) { 134 error = hammer_ioc_mirror_write(&trans, ip, 135 (struct hammer_ioc_mirror_rw *)data); 136 } 137 break; 138 case HAMMERIOC_GET_VERSION: 139 error = hammer_ioc_get_version(&trans, ip, 140 (struct hammer_ioc_version *)data); 141 break; 142 case HAMMERIOC_GET_INFO: 143 error = hammer_ioc_get_info(&trans, 144 (struct hammer_ioc_info *)data); 145 break; 146 case HAMMERIOC_SET_VERSION: 147 if (error == 0) { 148 error = hammer_ioc_set_version(&trans, ip, 149 (struct hammer_ioc_version *)data); 150 } 151 break; 152 case HAMMERIOC_EXPAND: 153 if (error == 0) { 154 error = priv_check_cred(cred, PRIV_HAMMER_EXPAND, 0); 155 if (error == 0) 156 error = hammer_ioc_expand(&trans, ip, 157 (struct hammer_ioc_expand *)data); 158 } 159 break; 160 161 default: 162 error = EOPNOTSUPP; 163 break; 164 } 165 hammer_done_transaction(&trans); 166 return (error); 167 } 168 169 /* 170 * Iterate through an object's inode or an object's records and record 171 * modification TIDs. 172 */ 173 static void add_history(hammer_inode_t ip, struct hammer_ioc_history *hist, 174 hammer_btree_elm_t elm); 175 176 static 177 int 178 hammer_ioc_gethistory(hammer_transaction_t trans, hammer_inode_t ip, 179 struct hammer_ioc_history *hist) 180 { 181 struct hammer_cursor cursor; 182 hammer_btree_elm_t elm; 183 int error; 184 185 /* 186 * Validate the structure and initialize for return. 187 */ 188 if (hist->beg_tid > hist->end_tid) 189 return(EINVAL); 190 if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) { 191 if (hist->key > hist->nxt_key) 192 return(EINVAL); 193 } 194 195 hist->obj_id = ip->obj_id; 196 hist->count = 0; 197 hist->nxt_tid = hist->end_tid; 198 hist->head.flags &= ~HAMMER_IOC_HISTORY_NEXT_TID; 199 hist->head.flags &= ~HAMMER_IOC_HISTORY_NEXT_KEY; 200 hist->head.flags &= ~HAMMER_IOC_HISTORY_EOF; 201 hist->head.flags &= ~HAMMER_IOC_HISTORY_UNSYNCED; 202 if ((ip->flags & HAMMER_INODE_MODMASK) & 203 ~(HAMMER_INODE_ATIME | HAMMER_INODE_MTIME)) { 204 hist->head.flags |= HAMMER_IOC_HISTORY_UNSYNCED; 205 } 206 207 /* 208 * Setup the cursor. We can't handle undeletable records 209 * (create_tid of 0) at the moment. A create_tid of 0 has 210 * a special meaning and cannot be specified in the cursor. 211 */ 212 error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL); 213 if (error) { 214 hammer_done_cursor(&cursor); 215 return(error); 216 } 217 218 cursor.key_beg.obj_id = hist->obj_id; 219 cursor.key_beg.create_tid = hist->beg_tid; 220 cursor.key_beg.delete_tid = 0; 221 cursor.key_beg.obj_type = 0; 222 if (cursor.key_beg.create_tid == HAMMER_MIN_TID) 223 cursor.key_beg.create_tid = 1; 224 225 cursor.key_end.obj_id = hist->obj_id; 226 cursor.key_end.create_tid = hist->end_tid; 227 cursor.key_end.delete_tid = 0; 228 cursor.key_end.obj_type = 0; 229 230 cursor.flags |= HAMMER_CURSOR_END_EXCLUSIVE; 231 232 if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) { 233 /* 234 * key-range within the file. For a regular file the 235 * on-disk key represents BASE+LEN, not BASE, so the 236 * first possible record containing the offset 'key' 237 * has an on-disk key of (key + 1). 238 */ 239 cursor.key_beg.key = hist->key; 240 cursor.key_end.key = HAMMER_MAX_KEY; 241 cursor.key_beg.localization = ip->obj_localization + 242 HAMMER_LOCALIZE_MISC; 243 cursor.key_end.localization = ip->obj_localization + 244 HAMMER_LOCALIZE_MISC; 245 246 switch(ip->ino_data.obj_type) { 247 case HAMMER_OBJTYPE_REGFILE: 248 ++cursor.key_beg.key; 249 cursor.key_beg.rec_type = HAMMER_RECTYPE_DATA; 250 break; 251 case HAMMER_OBJTYPE_DIRECTORY: 252 cursor.key_beg.rec_type = HAMMER_RECTYPE_DIRENTRY; 253 cursor.key_beg.localization = ip->obj_localization + 254 hammer_dir_localization(ip); 255 cursor.key_end.localization = ip->obj_localization + 256 hammer_dir_localization(ip); 257 break; 258 case HAMMER_OBJTYPE_DBFILE: 259 cursor.key_beg.rec_type = HAMMER_RECTYPE_DB; 260 break; 261 default: 262 error = EINVAL; 263 break; 264 } 265 cursor.key_end.rec_type = cursor.key_beg.rec_type; 266 } else { 267 /* 268 * The inode itself. 269 */ 270 cursor.key_beg.key = 0; 271 cursor.key_end.key = 0; 272 cursor.key_beg.rec_type = HAMMER_RECTYPE_INODE; 273 cursor.key_end.rec_type = HAMMER_RECTYPE_INODE; 274 cursor.key_beg.localization = ip->obj_localization + 275 HAMMER_LOCALIZE_INODE; 276 cursor.key_end.localization = ip->obj_localization + 277 HAMMER_LOCALIZE_INODE; 278 } 279 280 error = hammer_btree_first(&cursor); 281 while (error == 0) { 282 elm = &cursor.node->ondisk->elms[cursor.index]; 283 284 add_history(ip, hist, elm); 285 if (hist->head.flags & (HAMMER_IOC_HISTORY_NEXT_TID | 286 HAMMER_IOC_HISTORY_NEXT_KEY | 287 HAMMER_IOC_HISTORY_EOF)) { 288 break; 289 } 290 error = hammer_btree_iterate(&cursor); 291 } 292 if (error == ENOENT) { 293 hist->head.flags |= HAMMER_IOC_HISTORY_EOF; 294 error = 0; 295 } 296 hammer_done_cursor(&cursor); 297 return(error); 298 } 299 300 /* 301 * Add the scanned element to the ioctl return structure. Some special 302 * casing is required for regular files to accomodate how data ranges are 303 * stored on-disk. 304 */ 305 static void 306 add_history(hammer_inode_t ip, struct hammer_ioc_history *hist, 307 hammer_btree_elm_t elm) 308 { 309 int i; 310 311 if (elm->base.btype != HAMMER_BTREE_TYPE_RECORD) 312 return; 313 if ((hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) && 314 ip->ino_data.obj_type == HAMMER_OBJTYPE_REGFILE) { 315 /* 316 * Adjust nxt_key 317 */ 318 if (hist->nxt_key > elm->leaf.base.key - elm->leaf.data_len && 319 hist->key < elm->leaf.base.key - elm->leaf.data_len) { 320 hist->nxt_key = elm->leaf.base.key - elm->leaf.data_len; 321 } 322 if (hist->nxt_key > elm->leaf.base.key) 323 hist->nxt_key = elm->leaf.base.key; 324 325 /* 326 * Record is beyond MAXPHYS, there won't be any more records 327 * in the iteration covering the requested offset (key). 328 */ 329 if (elm->leaf.base.key >= MAXPHYS && 330 elm->leaf.base.key - MAXPHYS > hist->key) { 331 hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_KEY; 332 } 333 334 /* 335 * Data-range of record does not cover the key. 336 */ 337 if (elm->leaf.base.key - elm->leaf.data_len > hist->key) 338 return; 339 340 } else if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) { 341 /* 342 * Adjust nxt_key 343 */ 344 if (hist->nxt_key > elm->leaf.base.key && 345 hist->key < elm->leaf.base.key) { 346 hist->nxt_key = elm->leaf.base.key; 347 } 348 349 /* 350 * Record is beyond the requested key. 351 */ 352 if (elm->leaf.base.key > hist->key) 353 hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_KEY; 354 } 355 356 /* 357 * Add create_tid if it is in-bounds. 358 */ 359 i = hist->count; 360 if ((i == 0 || 361 elm->leaf.base.create_tid != hist->hist_ary[i - 1].tid) && 362 elm->leaf.base.create_tid >= hist->beg_tid && 363 elm->leaf.base.create_tid < hist->end_tid) { 364 if (hist->count == HAMMER_MAX_HISTORY_ELMS) { 365 hist->nxt_tid = elm->leaf.base.create_tid; 366 hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_TID; 367 return; 368 } 369 hist->hist_ary[i].tid = elm->leaf.base.create_tid; 370 hist->hist_ary[i].time32 = elm->leaf.create_ts; 371 ++hist->count; 372 } 373 374 /* 375 * Add delete_tid if it is in-bounds. Note that different portions 376 * of the history may have overlapping data ranges with different 377 * delete_tid's. If this case occurs the delete_tid may match the 378 * create_tid of a following record. XXX 379 * 380 * [ ] 381 * [ ] 382 */ 383 i = hist->count; 384 if (elm->leaf.base.delete_tid && 385 elm->leaf.base.delete_tid >= hist->beg_tid && 386 elm->leaf.base.delete_tid < hist->end_tid) { 387 if (i == HAMMER_MAX_HISTORY_ELMS) { 388 hist->nxt_tid = elm->leaf.base.delete_tid; 389 hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_TID; 390 return; 391 } 392 hist->hist_ary[i].tid = elm->leaf.base.delete_tid; 393 hist->hist_ary[i].time32 = elm->leaf.delete_ts; 394 ++hist->count; 395 } 396 } 397 398 /* 399 * Acquire synchronization TID 400 */ 401 static 402 int 403 hammer_ioc_synctid(hammer_transaction_t trans, hammer_inode_t ip, 404 struct hammer_ioc_synctid *std) 405 { 406 hammer_mount_t hmp = ip->hmp; 407 int error = 0; 408 409 switch(std->op) { 410 case HAMMER_SYNCTID_NONE: 411 std->tid = hmp->flusher.tid; /* inaccurate */ 412 break; 413 case HAMMER_SYNCTID_ASYNC: 414 hammer_queue_inodes_flusher(hmp, MNT_NOWAIT); 415 hammer_flusher_async(hmp, NULL); 416 std->tid = hmp->flusher.tid; /* inaccurate */ 417 break; 418 case HAMMER_SYNCTID_SYNC1: 419 hammer_queue_inodes_flusher(hmp, MNT_WAIT); 420 hammer_flusher_sync(hmp); 421 std->tid = hmp->flusher.tid; 422 break; 423 case HAMMER_SYNCTID_SYNC2: 424 hammer_queue_inodes_flusher(hmp, MNT_WAIT); 425 hammer_flusher_sync(hmp); 426 std->tid = hmp->flusher.tid; 427 hammer_flusher_sync(hmp); 428 break; 429 default: 430 error = EOPNOTSUPP; 431 break; 432 } 433 return(error); 434 } 435 436 /* 437 * Retrieve version info. 438 * 439 * Load min_version, wip_version, and max_versino. If cur_version is passed 440 * as 0 then load the current version into cur_version. Load the description 441 * for cur_version into the description array. 442 * 443 * Returns 0 on success, EINVAL if cur_version is non-zero and set to an 444 * unsupported value. 445 */ 446 static 447 int 448 hammer_ioc_get_version(hammer_transaction_t trans, hammer_inode_t ip, 449 struct hammer_ioc_version *ver) 450 { 451 int error = 0; 452 453 ver->min_version = HAMMER_VOL_VERSION_MIN; 454 ver->wip_version = HAMMER_VOL_VERSION_WIP; 455 ver->max_version = HAMMER_VOL_VERSION_MAX; 456 if (ver->cur_version == 0) 457 ver->cur_version = trans->hmp->version; 458 switch(ver->cur_version) { 459 case 1: 460 ksnprintf(ver->description, sizeof(ver->description), 461 "2.0 - First HAMMER release"); 462 break; 463 case 2: 464 ksnprintf(ver->description, sizeof(ver->description), 465 "2.3 - New directory entry layout"); 466 break; 467 default: 468 ksnprintf(ver->description, sizeof(ver->description), 469 "Unknown"); 470 error = EINVAL; 471 break; 472 } 473 return(error); 474 }; 475 476 /* 477 * Set version info 478 */ 479 static 480 int 481 hammer_ioc_set_version(hammer_transaction_t trans, hammer_inode_t ip, 482 struct hammer_ioc_version *ver) 483 { 484 struct hammer_cursor cursor; 485 hammer_volume_t volume; 486 int error; 487 488 if (ver->cur_version < trans->hmp->version) 489 return(EINVAL); 490 if (ver->cur_version == trans->hmp->version) 491 return(0); 492 if (ver->cur_version > HAMMER_VOL_VERSION_MAX) 493 return(EINVAL); 494 if (trans->hmp->ronly) 495 return(EROFS); 496 497 /* 498 * Update the root volume header and the version cached in 499 * the hammer_mount structure. 500 */ 501 error = hammer_init_cursor(trans, &cursor, NULL, NULL); 502 if (error) 503 goto failed; 504 hammer_sync_lock_sh(trans); 505 506 volume = hammer_get_root_volume(cursor.trans->hmp, &error); 507 KKASSERT(error == 0); 508 hammer_modify_volume_field(cursor.trans, volume, vol_version); 509 volume->ondisk->vol_version = ver->cur_version; 510 cursor.trans->hmp->version = ver->cur_version; 511 hammer_modify_volume_done(volume); 512 hammer_rel_volume(volume, 0); 513 514 hammer_sync_unlock(trans); 515 failed: 516 ver->head.error = error; 517 hammer_done_cursor(&cursor); 518 return(0); 519 } 520 521 /* 522 * Get information 523 */ 524 static 525 int 526 hammer_ioc_get_info(hammer_transaction_t trans, struct hammer_ioc_info *info) { 527 528 struct hammer_volume_ondisk *od = trans->hmp->rootvol->ondisk; 529 struct hammer_mount *hm = trans->hmp; 530 531 /* Fill the structure with the necessary information */ 532 _hammer_checkspace(hm, HAMMER_CHKSPC_WRITE, &info->rsvbigblocks); 533 info->rsvbigblocks = info->rsvbigblocks >> HAMMER_LARGEBLOCK_BITS; 534 strlcpy(info->vol_name, od->vol_name, sizeof(od->vol_name)); 535 536 info->vol_fsid = hm->fsid; 537 info->vol_fstype = od->vol_fstype; 538 info->version = hm->version; 539 540 info->inodes = od->vol0_stat_inodes; 541 info->bigblocks = od->vol0_stat_bigblocks; 542 info->freebigblocks = od->vol0_stat_freebigblocks; 543 info->nvolumes = hm->nvolumes; 544 545 return 0; 546 } 547 548