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/far.h> 28 #include <sys/far_impl.h> 29 30 struct far_enum { 31 far_t *fe_far; 32 uint64_t fe_parent_ino; 33 uint64_t fe_del_dir_cnt; 34 uint64_t fe_put_back_cnt; 35 far_dirent_t *fe_dirent_chain; 36 }; 37 38 struct far_file { 39 far_t *ff_far; 40 uint64_t ff_len; 41 uint64_t ff_last_byte; 42 uint64_t ff_ino; 43 far_path_t *ff_path; 44 far_dirent_t *ff_dirent; 45 }; 46 47 static int far_dirent_del_pass2(void *far_enump, char *name, uint64_t ino); 48 static int far_dirent_mod_pass2(void *far_enump, char *name, 49 uint64_t ino_old, uint64_t ino_new); 50 static int far_dirent_unmod_pass2(void *far_enump, char *name, uint64_t ino); 51 static int far_dir_del_pass2(far_t *f, uint64_t ino); 52 static int far_add_pass2(far_t *f, uint64_t ino); 53 static int far_mod_pass2(far_t *f, uint64_t ino); 54 55 far_ops_t _ops = { 56 .far_dirent_del = far_dirent_del_pass2, 57 .far_dirent_mod = far_dirent_mod_pass2, 58 .far_dirent_unmod = far_dirent_unmod_pass2, 59 .far_file_add = far_add_pass2, 60 .far_file_mod = far_mod_pass2, 61 .far_dir_add = far_add_pass2, 62 .far_dir_del = far_dir_del_pass2, 63 .far_dir_mod = far_mod_pass2 64 }; 65 66 int 67 far_start2(far_t *f, far_ops_t **ops) 68 { 69 f->f_pass = PASS_UNLINK; 70 *ops = &_ops; 71 72 return (0); 73 } 74 75 int 76 far_abort(far_t *f) 77 { 78 far_send_fini(f); 79 80 return (0); 81 } 82 83 int 84 far_end(far_t *f) 85 { 86 int ret; 87 int ret2; 88 89 far_send_end(f); 90 91 ret = far_count_fini(&f->f_link_add_cnt); 92 ret += far_count_fini(&f->f_del_dir_cnt); 93 ret += far_count_fini(&f->f_put_back_cnt); 94 95 ret2 = far_abort(f); 96 97 return (ret ? EIO : ret2); 98 } 99 100 static int 101 dir_del(far_t *f, uint64_t ino, uint64_t removed_entries) 102 { 103 far_info_t si; 104 uint64_t parent; 105 int ret; 106 int exists; 107 uint64_t new_count; 108 uint64_t left; 109 110 while (1) { 111 ret = far_get_info(f, ino, FAR_OLD, &si, FI_ATTR_GEN | 112 FI_ATTR_PARENT | FI_ATTR_NENTRIES); 113 if (ret) 114 return (ret); 115 116 if (si.si_parent > f->f_current_ino) 117 return (0); 118 119 if (removed_entries + 2 < si.si_nentries) /* '.' and '..' */ 120 return (0); 121 122 far_free_count(&f->f_del_dir_cnt, ino); 123 ret = far_send_rmdir(f, NULL, ino); 124 if (ret) 125 return (ret); 126 127 parent = si.si_parent; 128 exists = far_get_info(f, parent, FAR_NEW, &si, 129 FI_ATTR_MODE); 130 if (exists && exists != ENOENT) 131 return (exists); 132 133 ret = far_get_count(&f->f_put_back_cnt, parent, &left, NULL); 134 if (ret && ret != ENOENT) 135 return (ret); 136 if (left > 0) { 137 ret = far_add_count(&f->f_put_back_cnt, parent, -1, 0, 138 &left, NULL); 139 if (ret) 140 return (ret); 141 } 142 if ((int64_t)left < 0) 143 return (EINVAL); 144 145 if (exists == 0 && S_ISDIR(si.si_mode)) { 146 char *name; 147 uint64_t new_ino; 148 far_info_t si_new; 149 int new; 150 151 new = far_get_info(f, ino, FAR_NEW, &si_new, 152 FI_ATTR_MODE); 153 if (new && new != ENOENT) 154 return (new); 155 ret = far_find_entry(f, parent, ino, FAR_OLD, &name); 156 if (ret) 157 return (ret); 158 159 ret = far_lookup_entry(f, parent, name, 160 FAR_NEW, &new_ino); 161 if (ret && ret != ENOENT) { 162 far_free_name(name); 163 return (ret); 164 } 165 if (ret == 0 && new_ino != ino) { 166 far_dirent_t dirent = { 167 .fd_name = name, 168 .fd_parent_ino = parent 169 }; 170 171 ret = far_send_rename_from_tempname(f, &dirent, 172 ino, new_ino); 173 if (ret) { 174 far_free_name(name); 175 return (ret); 176 } 177 } else if (ret == 0 && new_ino == ino && 178 !S_ISDIR(si_new.si_mode)) { 179 far_dirent_t dirent = { 180 .fd_name = name, 181 .fd_parent_ino = parent 182 }; 183 184 ret = far_dirent_add_file(f, &dirent, ino, 185 si_new.si_mode, 0); 186 if (ret) { 187 far_free_name(name); 188 return (ret); 189 } 190 } 191 far_free_name(name); 192 } 193 194 if (left == 0) { 195 far_free_count(&f->f_put_back_cnt, parent); 196 if (exists == 0 && S_ISDIR(si.si_mode)) { 197 ret = far_send_mtime_update(f, NULL, parent); 198 if (ret) 199 return (ret); 200 } 201 } 202 203 if (exists == 0 && S_ISDIR(si.si_mode)) 204 return (0); 205 206 /* propagate deletion */ 207 ret = far_add_count(&f->f_del_dir_cnt, parent, 1, 0, 208 &new_count, NULL); 209 if (ret) 210 return (ret); 211 212 ino = parent; 213 removed_entries = new_count; 214 } 215 } 216 217 static int 218 enum_dir(far_t *f, uint64_t ino, uint64_t *pput_back_cnt, 219 uint64_t *pdel_dir_cnt) 220 { 221 int ret; 222 struct far_enum fe = { 223 .fe_far = f, 224 .fe_parent_ino = ino, 225 .fe_del_dir_cnt = 0, 226 .fe_put_back_cnt = 0 227 }; 228 229 ret = far_dir_contents(f, ino, &fe); 230 if (ret) 231 return (ret); 232 233 if (pput_back_cnt) 234 *pput_back_cnt = fe.fe_put_back_cnt; 235 if (pdel_dir_cnt) 236 *pdel_dir_cnt = fe.fe_del_dir_cnt; 237 238 if (fe.fe_put_back_cnt) { 239 ret = far_add_count(&f->f_put_back_cnt, ino, 240 fe.fe_put_back_cnt, 0, NULL, NULL); 241 if (ret) 242 return (ret); 243 } 244 245 return (0); 246 } 247 248 static int 249 dirent_del_file(struct far_enum *fe, far_dirent_t *dirent, 250 uint64_t ino, uint64_t remains) 251 { 252 int ret; 253 254 ret = far_send_unlink(fe->fe_far, dirent, ino); 255 if (ret) 256 return (ret); 257 258 if (remains != FAR_NO_INO) { 259 ret = far_send_rename_from_tempname(fe->fe_far, dirent, 260 ino, remains); 261 if (ret) 262 return (ret); 263 } 264 265 fe->fe_del_dir_cnt++; 266 267 return (0); 268 } 269 270 static int 271 dirent_del_dir(struct far_enum *fe, far_dirent_t *dirent, uint64_t ino, 272 uint64_t remains) 273 { 274 int ret; 275 far_info_t si; 276 far_info_t si_old; 277 int new; 278 int old; 279 far_t *f = fe->fe_far; 280 281 new = far_get_info(f, ino, FAR_NEW, &si, FI_ATTR_GEN | 282 FI_ATTR_MODE); 283 if (new && new != ENOENT) 284 return (new); 285 old = far_get_info(f, ino, FAR_OLD, &si_old, 286 FI_ATTR_NENTRIES | FI_ATTR_GEN | 287 FI_ATTR_PARENT); 288 if (old) 289 return (old); 290 291 /* new == 0 means the dir was renamed, which happened during pass 1 */ 292 if (new == ENOENT || 293 (si.si_gen != si_old.si_gen && !S_ISDIR(si.si_mode))) { 294 uint64_t cnt; 295 296 if (ino > f->f_current_ino) { 297 ++fe->fe_put_back_cnt; 298 return (0); 299 } 300 301 ret = far_get_count(&f->f_del_dir_cnt, ino, &cnt, 302 NULL); 303 if (ret && ret != ENOENT) 304 return (ret); 305 /* 2 for '.' and '..' */ 306 if (cnt + 2 < si_old.si_nentries) { 307 ++fe->fe_put_back_cnt; 308 return (0); 309 } 310 311 far_free_count(&f->f_del_dir_cnt, ino); 312 ret = far_send_rmdir(f, dirent, ino); 313 if (ret) 314 return (ret); 315 } 316 if (remains != FAR_NO_INO) { 317 ret = far_send_rename_from_tempname(f, dirent, ino, remains); 318 if (ret) 319 return (ret); 320 } 321 fe->fe_del_dir_cnt++; 322 if (new == 0 && si.si_gen != si_old.si_gen && !S_ISDIR(si.si_mode) && 323 si_old.si_parent == dirent->fd_parent_ino) { 324 uint64_t parent = si_old.si_parent; 325 far_info_t sip; 326 char *name = NULL; 327 328 ret = far_get_info(f, parent, FAR_OLD, &sip, 329 FI_ATTR_MODE); 330 if (ret && ret != ENOENT) 331 return (ret); 332 if (ret == 0 && S_ISDIR(sip.si_mode)) { 333 uint64_t old_ino; 334 335 ret = far_find_entry(f, parent, ino, FAR_OLD, &name); 336 if (ret) 337 return (ret); 338 339 ret = far_lookup_entry(f, parent, name, 340 FAR_NEW, &old_ino); 341 if (ret && ret != ENOENT) { 342 far_free_name(name); 343 return (ret); 344 } 345 if (ret == 0) { 346 ret = far_dirent_add_file(f, dirent, ino, 347 si.si_mode, 0); 348 if (ret) 349 return (ret); 350 } 351 } 352 } 353 354 return (0); 355 } 356 357 static int 358 dirent_del(struct far_enum *fe, far_dirent_t *dirent, 359 uint64_t ino, uint64_t remains) 360 { 361 far_info_t si; 362 int ret; 363 364 ret = far_get_info(fe->fe_far, ino, FAR_OLD, &si, FI_ATTR_MODE); 365 if (ret) 366 return (ret); 367 368 if (S_ISDIR(si.si_mode)) { 369 return (dirent_del_dir(fe, dirent, ino, remains)); 370 } else { 371 return (dirent_del_file(fe, dirent, ino, remains)); 372 } 373 } 374 375 static int 376 far_dirent_del_pass2(void *far_enump, char *name, uint64_t ino) 377 { 378 struct far_enum *fe = far_enump; 379 far_dirent_t dirent = { 380 .fd_name = name, 381 .fd_parent_ino = fe->fe_parent_ino, 382 .fd_prev = fe->fe_dirent_chain, 383 }; 384 385 return (dirent_del(fe, &dirent, ino, FAR_NO_INO)); 386 } 387 388 static int 389 far_dirent_mod_pass2(void *far_enump, char *name, 390 uint64_t ino_old, uint64_t ino_new) 391 { 392 struct far_enum *fe = far_enump; 393 far_dirent_t dirent = { 394 .fd_name = name, 395 .fd_parent_ino = fe->fe_parent_ino, 396 .fd_prev = fe->fe_dirent_chain, 397 }; 398 399 return (dirent_del(fe, &dirent, ino_old, ino_new)); 400 } 401 402 static int 403 far_dirent_unmod_pass2(void *far_enump, char *name, uint64_t ino) 404 { 405 struct far_enum *fe = far_enump; 406 far_t *f = fe->fe_far; 407 int ret; 408 uint64_t cnt; 409 far_info_t si_old; 410 far_info_t si_new; 411 far_dirent_t dirent = { 412 .fd_name = name, 413 .fd_parent_ino = fe->fe_parent_ino, 414 .fd_prev = fe->fe_dirent_chain, 415 }; 416 417 ret = far_get_count(&f->f_link_add_cnt, ino, &cnt, NULL); 418 if (ret) 419 return (ret == ENOENT ? 0 : ret); 420 421 ret = far_get_info(f, ino, FAR_OLD, &si_old, FI_ATTR_MODE); 422 if (ret) 423 return (ret); 424 425 if (S_ISDIR(si_old.si_mode)) { 426 return (dirent_del_dir(fe, &dirent, ino, 0)); 427 } 428 429 ret = far_get_info(f, ino, FAR_NEW, &si_new, FI_ATTR_MODE); 430 if (ret) 431 return (ret); 432 ret = far_send_unlink(f, &dirent, ino); 433 if (ret) 434 return (ret); 435 if (S_ISDIR(si_new.si_mode)) { 436 far_free_count(&f->f_link_add_cnt, ino); 437 ret = far_send_rename_from_tempname(f, &dirent, ino, ino); 438 if (ret) 439 return (ret); 440 ret = far_send_mtime_update(f, &dirent, ino); 441 } else { 442 ret = far_dirent_add_file(f, &dirent, ino, si_new.si_mode, 0); 443 } 444 445 return (ret); 446 } 447 448 static int 449 far_add_pass2(far_t *f, uint64_t ino) 450 { 451 f->f_current_ino = ino; 452 f->f_current_path = NULL; 453 454 return (far_send_mtime_update(f, NULL, ino)); 455 } 456 457 static int 458 far_dir_del_pass2(far_t *f, uint64_t ino) 459 { 460 uint64_t put_back_cnt; 461 uint64_t del_dir_cnt; 462 uint64_t new_count; 463 int ret; 464 465 f->f_current_ino = ino; 466 f->f_current_path = NULL; 467 468 ret = enum_dir(f, ino, &put_back_cnt, &del_dir_cnt); 469 if (ret) 470 return (ret); 471 472 ret = far_add_count(&f->f_del_dir_cnt, ino, del_dir_cnt, 473 0, &new_count, NULL); 474 if (ret) 475 return (ret); 476 477 if (put_back_cnt == 0) { 478 ret = dir_del(f, ino, new_count); 479 if (ret) 480 return (ret); 481 } 482 483 return (0); 484 } 485 486 static int 487 far_mod_pass2(far_t *f, uint64_t ino) 488 { 489 far_info_t si_old; 490 far_info_t si_new; 491 int ret; 492 uint64_t put_back_cnt = 0; 493 494 f->f_current_ino = ino; 495 f->f_current_path = NULL; 496 497 ret = far_get_info(f, ino, FAR_NEW, &si_new, FI_ATTR_SIZE | 498 FI_ATTR_MODE | FI_ATTR_GEN | FI_ATTR_UID | 499 FI_ATTR_GID | FI_ATTR_SIZE); 500 if (ret) 501 return (ret); 502 ret = far_get_info(f, ino, FAR_OLD, &si_old, FI_ATTR_GEN | 503 FI_ATTR_MODE | FI_ATTR_UID | 504 FI_ATTR_GID | FI_ATTR_SIZE); 505 if (ret) 506 return (ret); 507 508 if (!(S_ISDIR(si_old.si_mode) && S_ISDIR(si_new.si_mode)) && 509 si_new.si_gen != si_old.si_gen) { 510 if (S_ISDIR(si_old.si_mode)) { 511 ret = far_dir_del_pass2(f, ino); 512 if (ret) 513 return (ret); 514 } 515 return (far_add_pass2(f, ino)); 516 } 517 518 if (S_ISDIR(si_new.si_mode)) { 519 ret = enum_dir(f, ino, &put_back_cnt, NULL); 520 if (ret) 521 return (ret); 522 } 523 524 if (put_back_cnt) 525 return (0); 526 527 return (far_send_mtime_update(f, NULL, ino)); 528 }