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