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         far_dirent_t *fe_dirent_chain;
  34 };
  35 
  36 struct far_file {
  37         far_t *ff_far;
  38         uint64_t ff_len;
  39         uint64_t ff_last_byte;
  40         uint64_t ff_ino;
  41         far_path_t *ff_path;
  42         far_dirent_t *ff_dirent;
  43 };
  44 
  45 static int far_file_data_pass1(void *far_filep, void *data, uint64_t off,
  46     uint64_t len);
  47 static int far_dirent_add_pass1(void *far_enump, char *name, uint64_t ino);
  48 static int far_dirent_mod_pass1(void *far_enump, char *name,
  49     uint64_t ino_old, uint64_t ino_new);
  50 static int far_dir_add_pass1(far_t *f, uint64_t ino);
  51 static int far_mod_pass1(far_t *f, uint64_t ino);
  52 
  53 static far_ops_t _ops = {
  54         .far_dir_add = far_dir_add_pass1,
  55         .far_dir_mod = far_mod_pass1,
  56         .far_dirent_add = far_dirent_add_pass1,
  57         .far_dirent_mod = far_dirent_mod_pass1,
  58         .far_file_mod = far_mod_pass1,
  59         .far_file_data = far_file_data_pass1
  60 };
  61 
  62 static int far_file_add_genchange(far_t *f, uint64_t ino);
  63 
  64 int
  65 far_start(far_t *f, far_ops_t **ops)
  66 {
  67         int ret;
  68 
  69         f->f_pass = PASS_LINK;
  70         far_count_init(&f->f_link_add_cnt, "link_add_cnt");
  71         far_count_init(&f->f_del_dir_cnt, "del_dir_cnt");
  72         far_count_init(&f->f_put_back_cnt, "put_back_cnt");
  73         far_send_init(f);
  74 
  75         *ops = &_ops;
  76 
  77         ret = far_send_start(f);
  78         if (ret) {
  79                 far_abort(f);
  80                 return (ret);
  81         }
  82 
  83         return (0);
  84 }
  85 
  86 static int
  87 enum_dir(far_t *f, uint64_t ino, far_dirent_t *chain)
  88 {
  89         struct far_enum fe = {
  90                 .fe_far = f,
  91                 .fe_parent_ino = ino,
  92                 .fe_dirent_chain = chain
  93         };
  94 
  95         return (far_dir_contents(f, ino, &fe));
  96 }
  97 
  98 static int
  99 far_file_data_pass1(void *far_filep, void *data, uint64_t off, uint64_t len)
 100 {
 101         struct far_file *ff = far_filep;
 102 
 103         if (off + len > ff->ff_len)
 104                 len = ff->ff_len - off;
 105 
 106         ff->ff_last_byte = off + len;
 107 
 108         return far_send_file_data(ff->ff_far, &ff->ff_path,
 109             ff->ff_dirent, ff->ff_ino, off, len, data);
 110 }
 111 
 112 static int
 113 dirent_add_dir(far_t *f, far_dirent_t *dirent, uint64_t ino, int exists)
 114 {
 115         far_info_t si_old;
 116         far_info_t si_new;
 117         int ret;
 118 
 119         ret = far_get_info(f, ino, FAR_OLD, &si_old,
 120                             FI_ATTR_PARENT | FI_ATTR_GEN | FI_ATTR_MODE);
 121         if (ret && ret != ENOENT)
 122                 return (ret);
 123 
 124         if (ret == 0) {
 125                 ret = far_get_info(f, ino, FAR_NEW, &si_new,
 126                                     FI_ATTR_GEN | FI_ATTR_MODE);
 127                 if (ret)
 128                         return (ret);
 129 
 130                 if (si_old.si_gen == si_new.si_gen ||
 131                     (S_ISDIR(si_new.si_mode) && S_ISDIR(si_old.si_mode)))
 132                         return far_send_rename(f, dirent, ino,
 133                                                 si_old.si_parent, exists);
 134                 return (0);
 135         }
 136 
 137         if (ino > f->f_current_ino)
 138                 return (0);
 139 
 140         /* dir is new */
 141         ret = far_send_mkdir(f, dirent, ino, exists);
 142         if (ret)
 143                 return (ret);
 144 
 145         return (enum_dir(f, ino, dirent));
 146 }
 147 
 148 int
 149 far_dirent_add_file(far_t *f, far_dirent_t *dirent,
 150     uint64_t ino, uint64_t mode, int exists)
 151 {
 152         far_info_t si_old;
 153         far_info_t si_new;
 154         int ret;
 155         far_path_t *far_path;
 156         uint64_t new_count;
 157         uint64_t old_aux;
 158 
 159         ret = far_get_info(f, ino, FAR_OLD, &si_old,
 160                             FI_ATTR_GEN | FI_ATTR_PARENT);
 161         if (ret && ret != ENOENT)
 162                 return (ret);
 163 
 164         if (ret == 0) {
 165                 ret = far_get_info(f, ino, FAR_NEW, &si_new,
 166                                     FI_ATTR_GEN);
 167                 if (ret)
 168                         return (ret);
 169 
 170                 if (si_old.si_gen == si_new.si_gen)
 171                         return far_send_link(f, dirent, ino, si_old.si_parent,
 172                             FAR_OLD, exists);
 173         }
 174 
 175         /* file is new */
 176         ret = far_add_count(&f->f_link_add_cnt, ino, 1, dirent->fd_parent_ino,
 177             &new_count, &old_aux);
 178         if (ret)
 179                 return (ret);
 180 
 181         if (new_count == 1)
 182                 ret = far_send_create_file(f, dirent, ino,
 183                                             exists, &far_path);
 184         else
 185                 ret = far_send_link(f, dirent, ino, old_aux, FAR_NEW, exists);
 186         if (ret)
 187                 return (ret);
 188 
 189         ret = far_get_info(f, ino, FAR_NEW, &si_new,
 190                             FI_ATTR_SIZE | FI_ATTR_LINKS);
 191         ASSERT(ret == 0);
 192         if (new_count == 1 && S_ISREG(mode)) {
 193                 struct far_file ff;
 194 
 195                 ff.ff_ino = ino;
 196                 ff.ff_len = si_new.si_size;
 197                 ff.ff_far = f;
 198                 ff.ff_path = far_path;
 199                 ff.ff_dirent = dirent;
 200                 ff.ff_last_byte = 0;
 201                 ret = far_file_contents(f, ino, &ff);
 202                 far_path_free(ff.ff_path);
 203                 if (ret)
 204                         return (ret);
 205                 if (ff.ff_last_byte != si_new.si_size) {
 206                         /* sparse end */
 207                         ret = far_send_truncate(f, NULL, ino, si_new.si_size);
 208                         if (ret)
 209                                 return (ret);
 210                 }
 211         }
 212         if (new_count == si_new.si_nlinks)
 213                 far_free_count(&f->f_link_add_cnt, ino);
 214 
 215         return (0);
 216 }
 217 
 218 static int
 219 dirent_add(far_t *f, far_dirent_t *dirent, uint64_t ino, int exists)
 220 {
 221         far_info_t si;
 222         int ret;
 223 
 224         ret = far_get_info(f, ino, FAR_NEW, &si, FI_ATTR_MODE);
 225         if (ret)
 226                 return (ret);
 227 
 228         if (S_ISDIR(si.si_mode)) {
 229                 return (dirent_add_dir(f, dirent, ino, exists));
 230         } else {
 231                 return (far_dirent_add_file(f, dirent, ino, si.si_mode,
 232                     exists));
 233         }
 234 }
 235 
 236 static int
 237 far_dirent_add_pass1(void *far_enump, char *name, uint64_t ino)
 238 {
 239         struct far_enum *fe = far_enump;
 240         far_dirent_t dirent = {
 241                 .fd_name = name,
 242                 .fd_parent_ino = fe->fe_parent_ino,
 243                 .fd_prev = fe->fe_dirent_chain,
 244         };
 245 
 246         return (dirent_add(fe->fe_far, &dirent, ino, 0));
 247 }
 248 
 249 static int
 250 far_dirent_mod_pass1(void *far_enump, char *name,
 251     uint64_t ino_old, uint64_t ino_new)
 252 {
 253         struct far_enum *fe = far_enump;
 254         far_dirent_t dirent = {
 255                 .fd_name = name,
 256                 .fd_parent_ino = fe->fe_parent_ino,
 257                 .fd_prev = fe->fe_dirent_chain,
 258         };
 259 
 260         return (dirent_add(fe->fe_far, &dirent, ino_new, 1));
 261 }
 262 
 263 static int
 264 far_file_add_genchange(far_t *f, uint64_t ino)
 265 {
 266         int ret;
 267         char *name = NULL;
 268 
 269         f->f_current_ino = ino;
 270         f->f_current_path = NULL;
 271 
 272         /*
 273          * only called when generation has changed. TODO: move to own
 274          * function
 275          */
 276         far_info_t si_old;
 277         far_info_t si_new;
 278         int same_name = 0;
 279 
 280         ret = far_get_info(f, ino, FAR_OLD, &si_old, FI_ATTR_MODE |
 281                             FI_ATTR_LINKS | FI_ATTR_PARENT);
 282         if (ret)
 283                 return (ret);
 284         ret = far_get_info(f, ino, FAR_NEW, &si_new, FI_ATTR_MODE |
 285                             FI_ATTR_LINKS | FI_ATTR_PARENT);
 286         if (ret)
 287                 return (ret);
 288 
 289         if (si_old.si_nlinks > 1 && si_new.si_nlinks > 1)
 290                 return far_add_count(&f->f_link_add_cnt, ino, 0, 0, NULL,
 291                     NULL);
 292 
 293         if (S_ISDIR(si_old.si_mode))
 294                 return (0);
 295 
 296         far_which_t from;
 297         far_which_t to;
 298         uint64_t new_ino;
 299         uint64_t parent = si_new.si_parent;
 300 
 301         if (si_old.si_nlinks == 1) {
 302                 from = FAR_OLD;
 303                 to = FAR_NEW;
 304                 parent = si_old.si_parent;
 305         } else if (si_old.si_nlinks > 1 && si_new.si_nlinks == 1) {
 306                 from = FAR_NEW;
 307                 to = FAR_OLD;
 308                 parent = si_new.si_parent;
 309         } else {
 310                 return (EINVAL);
 311         }
 312 
 313         ret = far_find_entry(f, parent, ino, from, &name);
 314         if (ret)
 315                 return (ret);
 316 
 317         ret = far_lookup_entry(f, parent, name, to, &new_ino);
 318         if (ret && ret != ENOENT)
 319                 goto out;
 320         if (ret == 0 && new_ino == ino)
 321                 same_name = 1;
 322 
 323         if ((si_old.si_nlinks == 1 || si_new.si_nlinks == 1) && !same_name) {
 324                 ret = 0;
 325                 goto out;
 326         }
 327 
 328         far_dirent_t dirent = {
 329                 .fd_parent_ino = parent,
 330                 .fd_name = name,
 331                 .fd_prev = NULL
 332         };
 333 
 334         ret = far_send_unlink(f, &dirent, ino);
 335         if (ret)
 336                 goto out;
 337         ret = far_dirent_add_file(f, &dirent, ino, si_new.si_mode, 0);
 338 
 339 out:
 340         far_free_name(name);
 341         return (ret);
 342 }
 343 
 344 static int
 345 far_dir_add_pass1(far_t *f, uint64_t ino)
 346 {
 347         int ret;
 348         uint64_t parent;
 349         uint64_t first_parent = FAR_NO_INO;
 350         far_info_t si;
 351         far_dirent_t dirent;
 352         int same_name = 0;
 353         char *name = NULL;
 354 
 355         f->f_current_ino = ino;
 356         f->f_current_path = NULL;
 357 
 358         parent = ino;
 359         while (1) {
 360                 /* the new parent must exist, otherwise the fs is wrong */
 361                 ret = far_get_info(f, parent, FAR_NEW, &si,
 362                                     FI_ATTR_PARENT);
 363                 if (ret)
 364                         return (ret);
 365                 if (first_parent == FAR_NO_INO)
 366                         first_parent = si.si_parent;
 367 
 368                 ret = far_get_info(f, parent, FAR_OLD, &si, 0);
 369                 if (ret && ret != ENOENT)
 370                         return (ret);
 371                 if (ret != ENOENT)
 372                         break;
 373 
 374                 /*
 375                  * this check is only needed for a full send, on all
 376                  * incrementals the parent already exists and it breaks out
 377                  * above
 378                  */
 379                 if (parent == si.si_parent) {
 380                         first_parent = FAR_NO_INO;
 381                         break;
 382                 }
 383                 parent = si.si_parent;
 384 
 385                 if (parent > ino)
 386                         return (0);
 387         }
 388 
 389         /*
 390          * check for same-name
 391          */
 392         if (first_parent != FAR_NO_INO) {
 393                 ret = far_get_info(f, first_parent, FAR_OLD, &si,
 394                     FI_ATTR_MODE);
 395                 if (ret && ret != ENOENT)
 396                         return (ret);
 397                 if (ret == 0 && S_ISDIR(si.si_mode)) {
 398                         uint64_t old_ino;
 399 
 400                         ret = far_find_entry(f, first_parent, ino,
 401                             FAR_NEW, &name);
 402                         if (ret)
 403                                 return (ret);
 404 
 405                         ret = far_lookup_entry(f, first_parent, name,
 406                             FAR_OLD, &old_ino);
 407                         if (ret && ret != ENOENT) {
 408                                 goto out;
 409                         }
 410                         if (ret == 0) {
 411                                 same_name = 1;
 412                                 dirent.fd_name = name;
 413                                 dirent.fd_parent_ino = first_parent;
 414                                 dirent.fd_prev = NULL;
 415                                 if (old_ino == ino) {
 416                                         ret = far_add_count(&f->f_link_add_cnt,
 417                                             ino, 0, 0, NULL, NULL);
 418                                         if (ret)
 419                                                 goto out;
 420                                 }
 421                         }
 422                 }
 423         }
 424         /* dir is new */
 425         ret = far_send_mkdir(f, same_name ? &dirent : NULL, ino, same_name);
 426         if (ret)
 427                 goto out;
 428 
 429         ret = enum_dir(f, ino, NULL);
 430 
 431 out:
 432         far_free_name(name);
 433         return (ret);
 434 }
 435 
 436 static int
 437 far_mod_pass1(far_t *f, uint64_t ino)
 438 {
 439         far_info_t si_old;
 440         far_info_t si_new;
 441         int ret;
 442 
 443         f->f_current_ino = ino;
 444         f->f_current_path = NULL;
 445 
 446         ret = far_get_info(f, ino, FAR_NEW, &si_new, FI_ATTR_SIZE |
 447                             FI_ATTR_MODE | FI_ATTR_GEN | FI_ATTR_UID |
 448                             FI_ATTR_GID | FI_ATTR_SIZE);
 449         if (ret)
 450                 return (ret);
 451         ret = far_get_info(f, ino, FAR_OLD, &si_old, FI_ATTR_GEN |
 452                             FI_ATTR_MODE | FI_ATTR_UID |
 453                             FI_ATTR_GID | FI_ATTR_SIZE);
 454         if (ret)
 455                 return (ret);
 456 
 457         if (!(S_ISDIR(si_old.si_mode) && S_ISDIR(si_new.si_mode)) &&
 458             si_new.si_gen != si_old.si_gen) {
 459                 if (S_ISDIR(si_new.si_mode))
 460                         return (far_dir_add_pass1(f, ino));
 461                 else
 462                         return (far_file_add_genchange(f, ino));
 463         }
 464 
 465         if (S_ISDIR(si_new.si_mode)) {
 466                 ret = enum_dir(f, ino, NULL);
 467                 if (ret)
 468                         return (ret);
 469         }
 470 
 471         if (S_ISREG(si_new.si_mode)) {
 472                 struct far_file ff;
 473                 ff.ff_ino = ino;
 474                 ff.ff_len = si_new.si_size;
 475                 ff.ff_far = f;
 476                 ff.ff_path = NULL;
 477                 ff.ff_dirent = NULL;
 478                 ff.ff_last_byte = 0;
 479 
 480                 ret = far_file_contents(f, ino, &ff);
 481                 far_path_free(ff.ff_path);
 482                 if (ret)
 483                         return (ret);
 484                 if (si_new.si_size < si_old.si_size ||
 485                     (si_new.si_size != si_old.si_size &&
 486                     si_new.si_size != ff.ff_last_byte)) {
 487                         ret = far_send_truncate(f, NULL, ino, si_new.si_size);
 488                         if (ret)
 489                                 return (ret);
 490                 }
 491         }
 492 
 493         if (si_old.si_uid != si_new.si_uid || si_old.si_gid != si_new.si_gid) {
 494                 ret = far_send_chown(f, NULL, ino, si_new.si_uid,
 495                     si_new.si_gid);
 496                 if (ret)
 497                         return (ret);
 498         }
 499         if (si_old.si_mode != si_new.si_mode) {
 500                 ret = far_send_chmod(f, NULL, ino, si_new.si_mode);
 501                 if (ret)
 502                         return (ret);
 503         }
 504 
 505         return (ret);
 506 }