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 }