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.30 2008/07/31 04:42:04 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 44 int 45 hammer_ioctl(hammer_inode_t ip, u_long com, caddr_t data, int fflag, 46 struct ucred *cred) 47 { 48 struct hammer_transaction trans; 49 int error; 50 51 error = suser_cred(cred, PRISON_ROOT); 52 53 hammer_start_transaction(&trans, ip->hmp); 54 55 switch(com) { 56 case HAMMERIOC_PRUNE: 57 if (error == 0) { 58 error = hammer_ioc_prune(&trans, ip, 59 (struct hammer_ioc_prune *)data); 60 } 61 break; 62 case HAMMERIOC_GETHISTORY: 63 error = hammer_ioc_gethistory(&trans, ip, 64 (struct hammer_ioc_history *)data); 65 break; 66 case HAMMERIOC_REBLOCK: 67 if (error == 0) { 68 error = hammer_ioc_reblock(&trans, ip, 69 (struct hammer_ioc_reblock *)data); 70 } 71 break; 72 case HAMMERIOC_SYNCTID: 73 error = hammer_ioc_synctid(&trans, ip, 74 (struct hammer_ioc_synctid *)data); 75 break; 76 case HAMMERIOC_GET_PSEUDOFS: 77 error = hammer_ioc_get_pseudofs(&trans, ip, 78 (struct hammer_ioc_pseudofs_rw *)data); 79 break; 80 case HAMMERIOC_SET_PSEUDOFS: 81 if (error == 0) { 82 error = hammer_ioc_set_pseudofs(&trans, ip, cred, 83 (struct hammer_ioc_pseudofs_rw *)data); 84 } 85 break; 86 case HAMMERIOC_UPG_PSEUDOFS: 87 if (error == 0) { 88 error = hammer_ioc_upgrade_pseudofs(&trans, ip, 89 (struct hammer_ioc_pseudofs_rw *)data); 90 } 91 break; 92 case HAMMERIOC_DGD_PSEUDOFS: 93 if (error == 0) { 94 error = hammer_ioc_downgrade_pseudofs(&trans, ip, 95 (struct hammer_ioc_pseudofs_rw *)data); 96 } 97 break; 98 case HAMMERIOC_RMR_PSEUDOFS: 99 if (error == 0) { 100 error = hammer_ioc_destroy_pseudofs(&trans, ip, 101 (struct hammer_ioc_pseudofs_rw *)data); 102 } 103 break; 104 case HAMMERIOC_WAI_PSEUDOFS: 105 if (error == 0) { 106 error = hammer_ioc_wait_pseudofs(&trans, ip, 107 (struct hammer_ioc_pseudofs_rw *)data); 108 } 109 break; 110 case HAMMERIOC_MIRROR_READ: 111 if (error == 0) { 112 error = hammer_ioc_mirror_read(&trans, ip, 113 (struct hammer_ioc_mirror_rw *)data); 114 } 115 break; 116 case HAMMERIOC_MIRROR_WRITE: 117 if (error == 0) { 118 error = hammer_ioc_mirror_write(&trans, ip, 119 (struct hammer_ioc_mirror_rw *)data); 120 } 121 break; 122 default: 123 error = EOPNOTSUPP; 124 break; 125 } 126 hammer_done_transaction(&trans); 127 return (error); 128 } 129 130 /* 131 * Iterate through an object's inode or an object's records and record 132 * modification TIDs. 133 */ 134 static void add_history(hammer_inode_t ip, struct hammer_ioc_history *hist, 135 hammer_btree_elm_t elm); 136 137 static 138 int 139 hammer_ioc_gethistory(hammer_transaction_t trans, hammer_inode_t ip, 140 struct hammer_ioc_history *hist) 141 { 142 struct hammer_cursor cursor; 143 hammer_btree_elm_t elm; 144 int error; 145 146 /* 147 * Validate the structure and initialize for return. 148 */ 149 if (hist->beg_tid > hist->end_tid) 150 return(EINVAL); 151 if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) { 152 if (hist->key > hist->nxt_key) 153 return(EINVAL); 154 } 155 156 hist->obj_id = ip->obj_id; 157 hist->count = 0; 158 hist->nxt_tid = hist->end_tid; 159 hist->head.flags &= ~HAMMER_IOC_HISTORY_NEXT_TID; 160 hist->head.flags &= ~HAMMER_IOC_HISTORY_NEXT_KEY; 161 hist->head.flags &= ~HAMMER_IOC_HISTORY_EOF; 162 hist->head.flags &= ~HAMMER_IOC_HISTORY_UNSYNCED; 163 if ((ip->flags & HAMMER_INODE_MODMASK) & 164 ~(HAMMER_INODE_ATIME | HAMMER_INODE_MTIME)) { 165 hist->head.flags |= HAMMER_IOC_HISTORY_UNSYNCED; 166 } 167 168 /* 169 * Setup the cursor. We can't handle undeletable records 170 * (create_tid of 0) at the moment. A create_tid of 0 has 171 * a special meaning and cannot be specified in the cursor. 172 */ 173 error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL); 174 if (error) { 175 hammer_done_cursor(&cursor); 176 return(error); 177 } 178 179 cursor.key_beg.obj_id = hist->obj_id; 180 cursor.key_beg.create_tid = hist->beg_tid; 181 cursor.key_beg.delete_tid = 0; 182 cursor.key_beg.obj_type = 0; 183 if (cursor.key_beg.create_tid == HAMMER_MIN_TID) 184 cursor.key_beg.create_tid = 1; 185 186 cursor.key_end.obj_id = hist->obj_id; 187 cursor.key_end.create_tid = hist->end_tid; 188 cursor.key_end.delete_tid = 0; 189 cursor.key_end.obj_type = 0; 190 191 cursor.flags |= HAMMER_CURSOR_END_EXCLUSIVE; 192 193 if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) { 194 /* 195 * key-range within the file. For a regular file the 196 * on-disk key represents BASE+LEN, not BASE, so the 197 * first possible record containing the offset 'key' 198 * has an on-disk key of (key + 1). 199 */ 200 cursor.key_beg.key = hist->key; 201 cursor.key_end.key = HAMMER_MAX_KEY; 202 cursor.key_beg.localization = ip->obj_localization + 203 HAMMER_LOCALIZE_MISC; 204 cursor.key_end.localization = ip->obj_localization + 205 HAMMER_LOCALIZE_MISC; 206 207 switch(ip->ino_data.obj_type) { 208 case HAMMER_OBJTYPE_REGFILE: 209 ++cursor.key_beg.key; 210 cursor.key_beg.rec_type = HAMMER_RECTYPE_DATA; 211 break; 212 case HAMMER_OBJTYPE_DIRECTORY: 213 cursor.key_beg.rec_type = HAMMER_RECTYPE_DIRENTRY; 214 break; 215 case HAMMER_OBJTYPE_DBFILE: 216 cursor.key_beg.rec_type = HAMMER_RECTYPE_DB; 217 break; 218 default: 219 error = EINVAL; 220 break; 221 } 222 cursor.key_end.rec_type = cursor.key_beg.rec_type; 223 } else { 224 /* 225 * The inode itself. 226 */ 227 cursor.key_beg.key = 0; 228 cursor.key_end.key = 0; 229 cursor.key_beg.rec_type = HAMMER_RECTYPE_INODE; 230 cursor.key_end.rec_type = HAMMER_RECTYPE_INODE; 231 cursor.key_beg.localization = ip->obj_localization + 232 HAMMER_LOCALIZE_INODE; 233 cursor.key_end.localization = ip->obj_localization + 234 HAMMER_LOCALIZE_INODE; 235 } 236 237 error = hammer_btree_first(&cursor); 238 while (error == 0) { 239 elm = &cursor.node->ondisk->elms[cursor.index]; 240 241 add_history(ip, hist, elm); 242 if (hist->head.flags & (HAMMER_IOC_HISTORY_NEXT_TID | 243 HAMMER_IOC_HISTORY_NEXT_KEY | 244 HAMMER_IOC_HISTORY_EOF)) { 245 break; 246 } 247 error = hammer_btree_iterate(&cursor); 248 } 249 if (error == ENOENT) { 250 hist->head.flags |= HAMMER_IOC_HISTORY_EOF; 251 error = 0; 252 } 253 hammer_done_cursor(&cursor); 254 return(error); 255 } 256 257 /* 258 * Add the scanned element to the ioctl return structure. Some special 259 * casing is required for regular files to accomodate how data ranges are 260 * stored on-disk. 261 */ 262 static void 263 add_history(hammer_inode_t ip, struct hammer_ioc_history *hist, 264 hammer_btree_elm_t elm) 265 { 266 int i; 267 268 if (elm->base.btype != HAMMER_BTREE_TYPE_RECORD) 269 return; 270 if ((hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) && 271 ip->ino_data.obj_type == HAMMER_OBJTYPE_REGFILE) { 272 /* 273 * Adjust nxt_key 274 */ 275 if (hist->nxt_key > elm->leaf.base.key - elm->leaf.data_len && 276 hist->key < elm->leaf.base.key - elm->leaf.data_len) { 277 hist->nxt_key = elm->leaf.base.key - elm->leaf.data_len; 278 } 279 if (hist->nxt_key > elm->leaf.base.key) 280 hist->nxt_key = elm->leaf.base.key; 281 282 /* 283 * Record is beyond MAXPHYS, there won't be any more records 284 * in the iteration covering the requested offset (key). 285 */ 286 if (elm->leaf.base.key >= MAXPHYS && 287 elm->leaf.base.key - MAXPHYS > hist->key) { 288 hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_KEY; 289 } 290 291 /* 292 * Data-range of record does not cover the key. 293 */ 294 if (elm->leaf.base.key - elm->leaf.data_len > hist->key) 295 return; 296 297 } else if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) { 298 /* 299 * Adjust nxt_key 300 */ 301 if (hist->nxt_key > elm->leaf.base.key && 302 hist->key < elm->leaf.base.key) { 303 hist->nxt_key = elm->leaf.base.key; 304 } 305 306 /* 307 * Record is beyond the requested key. 308 */ 309 if (elm->leaf.base.key > hist->key) 310 hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_KEY; 311 } 312 313 /* 314 * Add create_tid if it is in-bounds. 315 */ 316 i = hist->count; 317 if ((i == 0 || 318 elm->leaf.base.create_tid != hist->hist_ary[i - 1].tid) && 319 elm->leaf.base.create_tid >= hist->beg_tid && 320 elm->leaf.base.create_tid < hist->end_tid) { 321 if (hist->count == HAMMER_MAX_HISTORY_ELMS) { 322 hist->nxt_tid = elm->leaf.base.create_tid; 323 hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_TID; 324 return; 325 } 326 hist->hist_ary[i].tid = elm->leaf.base.create_tid; 327 hist->hist_ary[i].time32 = elm->leaf.create_ts; 328 ++hist->count; 329 } 330 331 /* 332 * Add delete_tid if it is in-bounds. Note that different portions 333 * of the history may have overlapping data ranges with different 334 * delete_tid's. If this case occurs the delete_tid may match the 335 * create_tid of a following record. XXX 336 * 337 * [ ] 338 * [ ] 339 */ 340 i = hist->count; 341 if (elm->leaf.base.delete_tid && 342 elm->leaf.base.delete_tid >= hist->beg_tid && 343 elm->leaf.base.delete_tid < hist->end_tid) { 344 if (i == HAMMER_MAX_HISTORY_ELMS) { 345 hist->nxt_tid = elm->leaf.base.delete_tid; 346 hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_TID; 347 return; 348 } 349 hist->hist_ary[i].tid = elm->leaf.base.delete_tid; 350 hist->hist_ary[i].time32 = elm->leaf.delete_ts; 351 ++hist->count; 352 } 353 } 354 355 /* 356 * Acquire synchronization TID 357 */ 358 static 359 int 360 hammer_ioc_synctid(hammer_transaction_t trans, hammer_inode_t ip, 361 struct hammer_ioc_synctid *std) 362 { 363 hammer_mount_t hmp = ip->hmp; 364 int error = 0; 365 366 switch(std->op) { 367 case HAMMER_SYNCTID_NONE: 368 std->tid = hmp->flusher.tid; /* inaccurate */ 369 break; 370 case HAMMER_SYNCTID_ASYNC: 371 hammer_queue_inodes_flusher(hmp, MNT_NOWAIT); 372 hammer_flusher_async(hmp, NULL); 373 std->tid = hmp->flusher.tid; /* inaccurate */ 374 break; 375 case HAMMER_SYNCTID_SYNC1: 376 hammer_queue_inodes_flusher(hmp, MNT_WAIT); 377 hammer_flusher_sync(hmp); 378 std->tid = hmp->flusher.tid; 379 break; 380 case HAMMER_SYNCTID_SYNC2: 381 hammer_queue_inodes_flusher(hmp, MNT_WAIT); 382 hammer_flusher_sync(hmp); 383 std->tid = hmp->flusher.tid; 384 hammer_flusher_sync(hmp); 385 break; 386 default: 387 error = EOPNOTSUPP; 388 break; 389 } 390 return(error); 391 } 392 393