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