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