1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2012 STRATO AG. All rights reserved. 23 */ 24 #include <sys/zfs_context.h> 25 #include <sys/errno.h> 26 #include <sys/stat.h> 27 #include <sys/fits.h> 28 #include <sys/fits_impl.h> 29 30 struct fits_enum { 31 fits_t *fe_fits; 32 uint64_t fe_parent_ino; 33 uint64_t fe_del_dir_cnt; 34 uint64_t fe_put_back_cnt; 35 fits_dirent_t *fe_dirent_chain; 36 }; 37 38 struct fits_file { 39 fits_t *ff_fits; 40 uint64_t ff_len; 41 uint64_t ff_last_byte; 42 uint64_t ff_ino; 43 fits_path_t *ff_path; 44 fits_dirent_t *ff_dirent; 45 }; 46 47 static int fits_dirent_del_pass2(void *fits_enump, char *name, uint64_t ino); 48 static int fits_dirent_mod_pass2(void *fits_enump, char *name, 49 uint64_t ino_old, uint64_t ino_new); 50 static int fits_dirent_unmod_pass2(void *fits_enump, char *name, uint64_t ino); 51 static int fits_dir_del_pass2(fits_t *f, uint64_t ino); 52 static int fits_add_pass2(fits_t *f, uint64_t ino); 53 static int fits_mod_pass2(fits_t *f, uint64_t ino); 54 55 fits_ops_t _ops = { 56 .fits_dirent_del = fits_dirent_del_pass2, 57 .fits_dirent_mod = fits_dirent_mod_pass2, 58 .fits_dirent_unmod = fits_dirent_unmod_pass2, 59 .fits_file_add = fits_add_pass2, 60 .fits_file_mod = fits_mod_pass2, 61 .fits_dir_add = fits_add_pass2, 62 .fits_dir_del = fits_dir_del_pass2, 63 .fits_dir_mod = fits_mod_pass2 64 }; 65 66 int 67 fits_start2(fits_t *f, fits_ops_t **ops) 68 { 69 f->f_pass = PASS_UNLINK; 70 *ops = &_ops; 71 72 return (0); 73 } 74 75 int 76 fits_abort(fits_t *f) 77 { 78 fits_send_fini(f); 79 80 return (0); 81 } 82 83 int 84 fits_end(fits_t *f) 85 { 86 int ret; 87 int ret2; 88 89 fits_send_end(f); 90 91 ret = fits_assert_count_empty(&f->f_link_add_cnt); 92 ret += fits_assert_count_empty(&f->f_del_dir_cnt); 93 ret += fits_assert_count_empty(&f->f_put_back_cnt); 94 95 printf("!fits: fits_end calling fits_abort\n"); 96 ret2 = fits_abort(f); 97 98 return (ret ? ret : ret2); 99 } 100 101 static int 102 dir_del(fits_t *f, uint64_t ino, uint64_t removed_entries) 103 { 104 fits_info_t si; 105 uint64_t parent; 106 int ret; 107 int exists; 108 uint64_t new_count; 109 uint64_t left; 110 111 while (1) { 112 ret = fits_get_info(f, ino, FITS_OLD, &si, FI_ATTR_GEN | 113 FI_ATTR_PARENT | FI_ATTR_NENTRIES); 114 if (ret) 115 return (ret); 116 117 if (si.si_parent > f->f_current_ino) 118 return (0); 119 120 if (removed_entries + 2 < si.si_nentries) /* '.' and '..' */ 121 return (0); 122 123 fits_free_count(&f->f_del_dir_cnt, ino); 124 ret = fits_send_rmdir(f, NULL, ino); 125 if (ret) 126 return (ret); 127 128 parent = si.si_parent; 129 exists = fits_get_info(f, parent, FITS_NEW, &si, 130 FI_ATTR_MODE); 131 if (exists && exists != ENOENT) 132 return (exists); 133 134 ret = fits_get_count(&f->f_put_back_cnt, parent, &left, NULL); 135 if (ret && ret != ENOENT) 136 return (ret); 137 if (left > 0) { 138 ret = fits_add_count(&f->f_put_back_cnt, parent, -1, 0, 139 &left, NULL); 140 if (ret) 141 return (ret); 142 } 143 if ((int64_t)left < 0) 144 return (EINVAL); 145 146 if (exists == 0 && S_ISDIR(si.si_mode)) { 147 char *name; 148 uint64_t new_ino; 149 fits_info_t si_new; 150 int new; 151 152 new = fits_get_info(f, ino, FITS_NEW, &si_new, 153 FI_ATTR_MODE); 154 if (new && new != ENOENT) 155 return (new); 156 ret = fits_find_entry(f, parent, ino, FITS_OLD, &name); 157 if (ret) 158 return (ret); 159 160 ret = fits_lookup_entry(f, parent, name, 161 FITS_NEW, &new_ino); 162 if (ret && ret != ENOENT) { 163 fits_free_name(name); 164 return (ret); 165 } 166 if (ret == 0 && new_ino != ino) { 167 fits_dirent_t dirent = { 168 .fd_name = name, 169 .fd_parent_ino = parent 170 }; 171 172 ret = fits_send_rename_from_tempname(f, &dirent, 173 ino, new_ino); 174 if (ret) { 175 fits_free_name(name); 176 return (ret); 177 } 178 } else if (ret == 0 && new_ino == ino && 179 !S_ISDIR(si_new.si_mode)) { 180 fits_dirent_t dirent = { 181 .fd_name = name, 182 .fd_parent_ino = parent 183 }; 184 185 ret = fits_dirent_add_file(f, &dirent, ino, 186 si_new.si_mode, 0); 187 if (ret) { 188 fits_free_name(name); 189 return (ret); 190 } 191 } 192 fits_free_name(name); 193 } 194 195 if (left == 0) { 196 fits_free_count(&f->f_put_back_cnt, parent); 197 if (exists == 0 && S_ISDIR(si.si_mode)) { 198 ret = fits_send_mtime_update(f, NULL, parent); 199 if (ret) 200 return (ret); 201 } 202 } 203 204 if (exists == 0 && S_ISDIR(si.si_mode)) 205 return (0); 206 207 /* propagate deletion */ 208 ret = fits_add_count(&f->f_del_dir_cnt, parent, 1, 0, 209 &new_count, NULL); 210 if (ret) 211 return (ret); 212 213 ino = parent; 214 removed_entries = new_count; 215 } 216 } 217 218 static int 219 enum_dir(fits_t *f, uint64_t ino, uint64_t *pput_back_cnt, 220 uint64_t *pdel_dir_cnt) 221 { 222 int ret; 223 struct fits_enum fe = { 224 .fe_fits = f, 225 .fe_parent_ino = ino, 226 .fe_del_dir_cnt = 0, 227 .fe_put_back_cnt = 0 228 }; 229 230 ret = fits_dir_contents(f, ino, &fe); 231 if (ret) 232 return (ret); 233 234 if (pput_back_cnt) 235 *pput_back_cnt = fe.fe_put_back_cnt; 236 if (pdel_dir_cnt) 237 *pdel_dir_cnt = fe.fe_del_dir_cnt; 238 239 if (fe.fe_put_back_cnt) { 240 ret = fits_add_count(&f->f_put_back_cnt, ino, 241 fe.fe_put_back_cnt, 0, NULL, NULL); 242 if (ret) 243 return (ret); 244 } 245 246 return (0); 247 } 248 249 static int 250 dirent_del_file(struct fits_enum *fe, fits_dirent_t *dirent, 251 uint64_t ino, uint64_t remains) 252 { 253 int ret; 254 255 ret = fits_send_unlink(fe->fe_fits, dirent, ino); 256 if (ret) 257 return (ret); 258 259 if (remains != FITS_NO_INO) { 260 ret = fits_send_rename_from_tempname(fe->fe_fits, dirent, 261 ino, remains); 262 if (ret) 263 return (ret); 264 } 265 266 fe->fe_del_dir_cnt++; 267 268 return (0); 269 } 270 271 static int 272 dirent_del_dir(struct fits_enum *fe, fits_dirent_t *dirent, uint64_t ino, 273 uint64_t remains) 274 { 275 int ret; 276 fits_info_t si; 277 fits_info_t si_old; 278 int new; 279 int old; 280 fits_t *f = fe->fe_fits; 281 282 new = fits_get_info(f, ino, FITS_NEW, &si, FI_ATTR_GEN | 283 FI_ATTR_MODE); 284 if (new && new != ENOENT) 285 return (new); 286 old = fits_get_info(f, ino, FITS_OLD, &si_old, 287 FI_ATTR_NENTRIES | FI_ATTR_GEN | 288 FI_ATTR_PARENT); 289 if (old) 290 return (old); 291 292 /* new == 0 means the dir was renamed, which happened during pass 1 */ 293 if (new == ENOENT || 294 (si.si_gen != si_old.si_gen && !S_ISDIR(si.si_mode))) { 295 uint64_t cnt; 296 297 if (ino > f->f_current_ino) { 298 ++fe->fe_put_back_cnt; 299 return (0); 300 } 301 302 ret = fits_get_count(&f->f_del_dir_cnt, ino, &cnt, 303 NULL); 304 if (ret && ret != ENOENT) 305 return (ret); 306 /* 2 for '.' and '..' */ 307 if (cnt + 2 < si_old.si_nentries) { 308 ++fe->fe_put_back_cnt; 309 return (0); 310 } 311 312 fits_free_count(&f->f_del_dir_cnt, ino); 313 ret = fits_send_rmdir(f, dirent, ino); 314 if (ret) 315 return (ret); 316 } 317 if (remains != FITS_NO_INO) { 318 ret = fits_send_rename_from_tempname(f, dirent, ino, remains); 319 if (ret) 320 return (ret); 321 } 322 fe->fe_del_dir_cnt++; 323 if (new == 0 && si.si_gen != si_old.si_gen && !S_ISDIR(si.si_mode) && 324 si_old.si_parent == dirent->fd_parent_ino) { 325 uint64_t parent = si_old.si_parent; 326 fits_info_t sip; 327 char *name = NULL; 328 329 ret = fits_get_info(f, parent, FITS_OLD, &sip, 330 FI_ATTR_MODE); 331 if (ret && ret != ENOENT) 332 return (ret); 333 if (ret == 0 && S_ISDIR(sip.si_mode)) { 334 uint64_t old_ino; 335 336 ret = fits_find_entry(f, parent, ino, FITS_OLD, &name); 337 if (ret) 338 return (ret); 339 340 ret = fits_lookup_entry(f, parent, name, 341 FITS_NEW, &old_ino); 342 if (ret && ret != ENOENT) { 343 fits_free_name(name); 344 return (ret); 345 } 346 if (ret == 0) { 347 ret = fits_dirent_add_file(f, dirent, ino, 348 si.si_mode, 0); 349 if (ret) 350 return (ret); 351 } 352 } 353 } 354 355 return (0); 356 } 357 358 static int 359 dirent_del(struct fits_enum *fe, fits_dirent_t *dirent, 360 uint64_t ino, uint64_t remains) 361 { 362 fits_info_t si; 363 int ret; 364 365 ret = fits_get_info(fe->fe_fits, ino, FITS_OLD, &si, FI_ATTR_MODE); 366 if (ret) 367 return (ret); 368 369 if (S_ISDIR(si.si_mode)) { 370 return (dirent_del_dir(fe, dirent, ino, remains)); 371 } else { 372 return (dirent_del_file(fe, dirent, ino, remains)); 373 } 374 } 375 376 static int 377 fits_dirent_del_pass2(void *fits_enump, char *name, uint64_t ino) 378 { 379 struct fits_enum *fe = fits_enump; 380 fits_dirent_t dirent = { 381 .fd_name = name, 382 .fd_parent_ino = fe->fe_parent_ino, 383 .fd_prev = fe->fe_dirent_chain, 384 }; 385 386 return (dirent_del(fe, &dirent, ino, FITS_NO_INO)); 387 } 388 389 static int 390 fits_dirent_mod_pass2(void *fits_enump, char *name, 391 uint64_t ino_old, uint64_t ino_new) 392 { 393 struct fits_enum *fe = fits_enump; 394 fits_dirent_t dirent = { 395 .fd_name = name, 396 .fd_parent_ino = fe->fe_parent_ino, 397 .fd_prev = fe->fe_dirent_chain, 398 }; 399 400 return (dirent_del(fe, &dirent, ino_old, ino_new)); 401 } 402 403 static int 404 fits_dirent_unmod_pass2(void *fits_enump, char *name, uint64_t ino) 405 { 406 struct fits_enum *fe = fits_enump; 407 fits_t *f = fe->fe_fits; 408 int ret; 409 uint64_t cnt; 410 fits_info_t si_old; 411 fits_info_t si_new; 412 fits_dirent_t dirent = { 413 .fd_name = name, 414 .fd_parent_ino = fe->fe_parent_ino, 415 .fd_prev = fe->fe_dirent_chain, 416 }; 417 418 ret = fits_get_count(&f->f_link_add_cnt, ino, &cnt, NULL); 419 if (ret) 420 return (ret == ENOENT ? 0 : ret); 421 422 ret = fits_get_info(f, ino, FITS_OLD, &si_old, FI_ATTR_MODE); 423 if (ret) 424 return (ret); 425 426 if (S_ISDIR(si_old.si_mode)) { 427 return (dirent_del_dir(fe, &dirent, ino, 0)); 428 } 429 430 ret = fits_get_info(f, ino, FITS_NEW, &si_new, FI_ATTR_MODE); 431 if (ret) 432 return (ret); 433 ret = fits_send_unlink(f, &dirent, ino); 434 if (ret) 435 return (ret); 436 if (S_ISDIR(si_new.si_mode)) { 437 fits_free_count(&f->f_link_add_cnt, ino); 438 ret = fits_send_rename_from_tempname(f, &dirent, ino, ino); 439 if (ret) 440 return (ret); 441 ret = fits_send_mtime_update(f, &dirent, ino); 442 } else { 443 ret = fits_dirent_add_file(f, &dirent, ino, si_new.si_mode, 0); 444 } 445 446 return (ret); 447 } 448 449 static int 450 fits_add_pass2(fits_t *f, uint64_t ino) 451 { 452 f->f_current_ino = ino; 453 f->f_current_path = NULL; 454 455 return (fits_send_mtime_update(f, NULL, ino)); 456 } 457 458 static int 459 fits_dir_del_pass2(fits_t *f, uint64_t ino) 460 { 461 uint64_t put_back_cnt; 462 uint64_t del_dir_cnt; 463 uint64_t new_count; 464 int ret; 465 466 f->f_current_ino = ino; 467 f->f_current_path = NULL; 468 469 ret = enum_dir(f, ino, &put_back_cnt, &del_dir_cnt); 470 if (ret) 471 return (ret); 472 473 ret = fits_add_count(&f->f_del_dir_cnt, ino, del_dir_cnt, 474 0, &new_count, NULL); 475 if (ret) 476 return (ret); 477 478 if (put_back_cnt == 0) { 479 ret = dir_del(f, ino, new_count); 480 if (ret) 481 return (ret); 482 } 483 484 return (0); 485 } 486 487 static int 488 fits_mod_pass2(fits_t *f, uint64_t ino) 489 { 490 fits_info_t si_old; 491 fits_info_t si_new; 492 int ret; 493 uint64_t put_back_cnt = 0; 494 495 f->f_current_ino = ino; 496 f->f_current_path = NULL; 497 498 ret = fits_get_info(f, ino, FITS_NEW, &si_new, FI_ATTR_SIZE | 499 FI_ATTR_MODE | FI_ATTR_GEN | FI_ATTR_UID | 500 FI_ATTR_GID | FI_ATTR_SIZE); 501 if (ret) 502 return (ret); 503 ret = fits_get_info(f, ino, FITS_OLD, &si_old, FI_ATTR_GEN | 504 FI_ATTR_MODE | FI_ATTR_UID | 505 FI_ATTR_GID | FI_ATTR_SIZE); 506 if (ret) 507 return (ret); 508 509 if (!(S_ISDIR(si_old.si_mode) && S_ISDIR(si_new.si_mode)) && 510 si_new.si_gen != si_old.si_gen) { 511 if (S_ISDIR(si_old.si_mode)) { 512 ret = fits_dir_del_pass2(f, ino); 513 if (ret) 514 return (ret); 515 } 516 return (fits_add_pass2(f, ino)); 517 } 518 519 if (S_ISDIR(si_new.si_mode)) { 520 ret = enum_dir(f, ino, &put_back_cnt, NULL); 521 if (ret) 522 return (ret); 523 } 524 525 if (put_back_cnt) 526 return (0); 527 528 return (fits_send_mtime_update(f, NULL, ino)); 529 }