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 }