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