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/stat.h>
  26 #include <sys/mkdev.h>
  27 #include <sys/errno.h>
  28 #include <sys/types.h>
  29 #include <sys/fits.h>
  30 #include <sys/fits_impl.h>
  31 #include <sys/fits_crc32c.h>
  32 
  33 #define TEMPNAME_PREFIX "fits-tempname-"
  34 /* 2^128 needs 39 digits in decimal */
  35 #define TEMPNAME_SIZE (sizeof (TEMPNAME_PREFIX) + 39)
  36 
  37 void
  38 fits_send_init(fits_t *f)
  39 {
  40         f->f_alloc_len = FITS_SEND_BUF_SIZE;
  41         f->f_buf = kmem_alloc(f->f_alloc_len, KM_SLEEP);
  42         f->f_size = 0;
  43 }
  44 
  45 void
  46 fits_send_fini(fits_t *f)
  47 {
  48         kmem_free(f->f_buf, f->f_alloc_len);
  49 }
  50 
  51 static int
  52 fits_send_reserve(fits_t *f, void **buf, int len)
  53 {
  54         int res = f->f_alloc_len - f->f_size;
  55         if (len > res)
  56                 return (-E2BIG);
  57         *buf = f->f_buf + f->f_size;
  58         f->f_size += len;
  59 
  60         return (0);
  61 }
  62 
  63 static int
  64 fits_send_put(fits_t *f, void *buf, int len)
  65 {
  66         int ret;
  67         void *p;
  68 
  69         ret = fits_send_reserve(f, &p, len);
  70         if (ret)
  71                 return (ret);
  72 
  73         memcpy(p, buf, len);
  74 
  75         return (0);
  76 }
  77 
  78 static int
  79 fits_send_put_attr(fits_t *f, uint16_t attr, void *buf, int len)
  80 {
  81         fits_attr_header_t hdr;
  82         int ret;
  83 
  84         LE_OUT16(&hdr.fa_type, attr);
  85         LE_OUT16(&hdr.fa_len, len);
  86 
  87         ret = fits_send_put(f, &hdr, sizeof (hdr));
  88         if (ret)
  89                 return (ret);
  90         return (fits_send_put(f, buf, len));
  91 }
  92 
  93 static int
  94 fits_send_reserve_attr(fits_t *f, uint16_t attr, void **buf, int len)
  95 {
  96         fits_attr_header_t hdr;
  97         int ret;
  98 
  99         LE_OUT16(&hdr.fa_type, attr);
 100         LE_OUT16(&hdr.fa_len, len);
 101 
 102         ret = fits_send_put(f, &hdr, sizeof (hdr));
 103         if (ret)
 104                 return (ret);
 105         return (fits_send_reserve(f, buf, len));
 106 }
 107 
 108 static int
 109 fits_send_put_u64(fits_t *f, uint16_t attr, uint64_t val)
 110 {
 111         uint64_t v;
 112 
 113         LE_OUT64(&v, val);
 114         return (fits_send_put_attr(f, attr, &v, sizeof (v)));
 115 }
 116 
 117 static int
 118 fits_send_put_time(fits_t *f, uint16_t attr, fits_time_t *t)
 119 {
 120         char buf[12];
 121 
 122         LE_OUT64(buf, t->st_sec);
 123         LE_OUT32(buf + 8, t->st_nsec);
 124 
 125         return (fits_send_put_attr(f, attr, buf, sizeof (buf)));
 126 }
 127 
 128 static int
 129 fits_cmd_start(fits_t *f, uint16_t cmd)
 130 {
 131         fits_cmd_header_t ch;
 132 
 133         memset(&ch, 0, sizeof (ch));
 134         LE_OUT16(&ch.fc_cmd, cmd);
 135         f->f_size = 0;
 136         return (fits_send_put(f, &ch, sizeof (ch)));
 137 }
 138 
 139 static int
 140 fits_cmd_send(fits_t *f)
 141 {
 142         fits_cmd_header_t *ch;
 143         uint32_t crc;
 144         int ret;
 145 
 146         ch = (fits_cmd_header_t *)f->f_buf;
 147         LE_OUT32(&ch->fc_len, f->f_size - sizeof (*ch));
 148         ch->fc_crc = 0;
 149 
 150         crc = fits_crc32c(0, f->f_buf, f->f_size);
 151         LE_OUT32(&ch->fc_crc, crc);
 152 
 153         ret = fits_write(f, f->f_buf, f->f_size);
 154         f->f_size = 0;
 155 
 156         return (ret);
 157 }
 158 
 159 static int
 160 fits_send_stream_header(fits_t *f)
 161 {
 162         fits_stream_header_t header;
 163 
 164         strcpy(header.fs_magic, FITS_SEND_STREAM_MAGIC);
 165         LE_OUT32(&header.fs_version, FITS_SEND_STREAM_VERSION);
 166 
 167         return (fits_write(f, (uint8_t *)&header, sizeof (header)));
 168 }
 169 
 170 static void
 171 tempname(uint64_t ino, char *buf, int maxlen)
 172 {
 173         int l = sizeof (TEMPNAME_PREFIX) - 1;
 174         memcpy(buf, TEMPNAME_PREFIX, MIN(maxlen, l));
 175         snprintf(buf + l, maxlen - l, "%llu", (long long)ino);
 176 }
 177 
 178 static void
 179 path_add_name(fits_path_t **fp, char *name, int namelen)
 180 {
 181         fits_path_t *new;
 182 
 183         new = kmem_alloc(sizeof (*new) + namelen + 1, KM_SLEEP);
 184         new->fp_next = *fp;
 185         new->fp_len = namelen + 1;
 186         new->fp_total_len = namelen + 1;
 187         if (*fp)
 188                 new->fp_total_len += (*fp)->fp_total_len;
 189         memcpy(new->fp_buf, name, namelen);
 190         new->fp_buf[namelen] = '\0';
 191         *fp = new;
 192 }
 193 
 194 static void
 195 path_copy(fits_path_t *fp, char *b)
 196 {
 197         fits_path_t *cur;
 198 
 199         for (cur = fp; cur; cur = cur->fp_next) {
 200                 *b = '/';
 201                 memcpy(b + 1, cur->fp_buf, cur->fp_len - 1);
 202                 b += cur->fp_len;
 203         }
 204 }
 205 
 206 static void
 207 path2buf(fits_path_t *fp, char **buf, int *buf_len)
 208 {
 209         char *b;
 210 
 211         *buf_len = fp->fp_total_len + 1; /* one for the trailing 0-byte */
 212         *buf = b = kmem_alloc(*buf_len, KM_SLEEP);
 213 
 214         path_copy(fp, b);
 215 
 216         b[*buf_len - 1] = '\0';
 217 }
 218 
 219 static int
 220 put_path(fits_t *f, uint16_t attr, fits_path_t *fp)
 221 {
 222         int ret;
 223         void *p;
 224 
 225         ret = fits_send_reserve_attr(f, attr, &p, fp->fp_total_len);
 226         if (ret)
 227                 return (ret);
 228         path_copy(fp, p);
 229 
 230         return (0);
 231 }
 232 
 233 void
 234 fits_path_free(fits_path_t *fp)
 235 {
 236         fits_path_t *next;
 237         while (fp) {
 238                 next = fp->fp_next;
 239                 kmem_free(fp, fp->fp_len + sizeof (*fp));
 240                 fp = next;
 241         }
 242 }
 243 
 244 static int
 245 is_ino_run(fits_t *f, uint64_t ino)
 246 {
 247         int ret;
 248         fits_info_t si;
 249 
 250         while (1) {
 251                 if (ino > f->f_current_ino)
 252                         return (0);
 253                 ret = fits_get_info(f, ino, FITS_OLD, &si, 0);
 254                 if (ret && ret != ENOENT)
 255                         return (ret);
 256                 if (ret != ENOENT)
 257                         break;
 258                 ret = fits_get_info(f, ino, FITS_NEW, &si, FI_ATTR_PARENT);
 259                 if (ret && ret != ENOENT)
 260                         return (ret);
 261                 if (ret)
 262                         return (0);     /* ignore for now */
 263                 ino = si.si_parent;
 264         }
 265         return (1);
 266 }
 267 
 268 static int
 269 build_path(fits_t *f, fits_dirent_t *dirent, uint64_t ino,
 270     int devise_tempname, fits_which_t which_in, fits_path_t **fp)
 271 {
 272         int ret = 0;
 273         fits_dirent_t *de;
 274         fits_info_t si;
 275         fits_which_t which;
 276         fits_dirent_t temp_dirent;
 277         char temp_buf[TEMPNAME_SIZE];
 278 
 279         if (devise_tempname) {
 280                 if (!dirent)
 281                         return (EINVAL);
 282                 temp_dirent = *dirent;
 283                 tempname(ino, temp_buf, sizeof (temp_buf));
 284                 temp_dirent.fd_name = temp_buf;
 285                 dirent = &temp_dirent;
 286         }
 287 
 288         *fp = NULL;
 289 
 290         for (de = dirent; de; de = de->fd_prev) {
 291                 path_add_name(fp, de->fd_name, strlen(de->fd_name));
 292                 ino = de->fd_parent_ino;
 293         }
 294 
 295         /*
 296          * XXX TODO check if f->f_current_path is set. if yes, use it instead.
 297          * otherwise save result of loop below to f_current_path
 298          */
 299         while (1) {
 300                 int namebuflen;
 301                 char *name;
 302                 char *t_name;
 303                 uint64_t old_parent;
 304                 uint64_t new_parent;
 305                 uint64_t old_gen = 0;
 306                 uint64_t new_gen = 0;
 307                 uint64_t parent;
 308                 int check_tempname;
 309                 uint64_t old_mode = 0;
 310 
 311                 old_parent = 0;
 312                 new_parent = 0;
 313                 check_tempname = 0;
 314                 ret = fits_get_info(f, ino, FITS_OLD, &si, FI_ATTR_PARENT |
 315                     FI_ATTR_GEN | FI_ATTR_MODE);
 316                 if (ret && ret != ENOENT)
 317                         return (ret);
 318                 if (ret == 0) {
 319                         old_parent = si.si_parent;
 320                         old_gen = si.si_gen;
 321                         old_mode = si.si_mode;
 322                 }
 323                 ret = fits_get_info(f, ino, FITS_NEW, &si, FI_ATTR_PARENT |
 324                     FI_ATTR_GEN | FI_ATTR_MODE);
 325                 if (ret && ret != ENOENT)
 326                         return (ret);
 327                 if (ret == 0) {
 328                         new_parent = si.si_parent;
 329                         new_gen = si.si_gen;
 330                 }
 331                 if (old_parent && new_parent && old_gen != new_gen &&
 332                     !(S_ISDIR(old_mode) && S_ISDIR(si.si_mode))) {
 333                         if (which_in == FITS_OLD) {
 334                                 new_parent = 0;
 335                         } else if (which_in == FITS_NEW) {
 336                                 old_parent = 0;
 337                                 if (S_ISDIR(si.si_mode))
 338                                         check_tempname = 1;
 339                         }
 340                 }
 341 
 342                 if (f->f_pass == PASS_LINK) {
 343                         if (old_parent && !new_parent) {
 344                                 which = FITS_OLD;
 345                         } else if (!old_parent && new_parent) {
 346                                 which = FITS_NEW;
 347                         } else if (is_ino_run(f, new_parent)) {
 348                                 check_tempname = 1;
 349                                 which = FITS_NEW;
 350                         } else {
 351                                 which = FITS_OLD;
 352                         }
 353                 } else {
 354                         if (old_parent && !new_parent) {
 355                                 which = FITS_OLD;
 356                         } else {
 357                                 check_tempname = 1;
 358                                 which = FITS_NEW;
 359                         }
 360                 }
 361                 if (which == FITS_OLD)
 362                         parent = old_parent;
 363                 else
 364                         parent = new_parent;
 365                 if (parent == ino)
 366                         break;
 367                 ret = fits_find_entry(f, parent, ino, which, &t_name);
 368                 if (ret)
 369                         return (ret);
 370                 name = strdup(t_name);
 371                 fits_free_name(t_name);
 372                 namebuflen = strlen(name) + 1;
 373                 if (check_tempname) {
 374                         fits_info_t si_old;
 375                         fits_info_t si_new;
 376 
 377                         ret = fits_get_info(f, parent, FITS_OLD,
 378                                             &si_old, FI_ATTR_GEN);
 379                         if (ret && ret != ENOENT)
 380                                 return (ret);
 381                         if (ret == 0) {
 382                                 ret = fits_get_info(f, parent, FITS_NEW,
 383                                                     &si_new, FI_ATTR_GEN);
 384                                 if (ret)
 385                                         return (ret);
 386                                 if (si_old.si_gen != si_new.si_gen)
 387                                         check_tempname = 0;
 388                         } else {
 389                                 check_tempname = 0;
 390                         }
 391                 }
 392                 if (check_tempname) {
 393                         uint64_t old_ino;
 394 
 395                         ret = fits_lookup_entry(f, parent, name,
 396                                                 FITS_OLD, &old_ino);
 397                         if (ret && ret != ENOENT) {
 398                                 fits_free_name(name);
 399                                 return (ret);
 400                         }
 401                         if ((ret == 0 && old_ino != ino) ||
 402                             (ret == 0 && S_ISDIR(si.si_mode) &&
 403                             !S_ISDIR(old_mode) && old_ino == ino)) {
 404                                 int ret;
 405                                 uint64_t cnt = 1;
 406 
 407                                 if (f->f_pass == PASS_UNLINK &&
 408                                     new_parent < f->f_current_ino) {
 409                                         ret = fits_get_count(&f->f_put_back_cnt,
 410                                             old_ino, &cnt, NULL);
 411                                         if (ret && ret != ENOENT)
 412                                                 return (ret);
 413                                 }
 414                                 if (cnt) {
 415                                         kmem_free(name, namebuflen);
 416                                         namebuflen = TEMPNAME_SIZE;
 417                                         name = kmem_alloc(namebuflen, KM_SLEEP);
 418                                         tempname(ino, name, namebuflen);
 419                                 }
 420                         }
 421                 }
 422                 ino = parent;
 423                 path_add_name(fp, name, strlen(name));
 424                 kmem_free(name, namebuflen);
 425         }
 426         if (*fp == NULL)
 427                 path_add_name(fp, "", 0);
 428 
 429         return (0);
 430 }
 431 
 432 int
 433 fits_send_start(fits_t *f)
 434 {
 435         int ret;
 436         uint8_t o_uuid[16];
 437         uint8_t n_uuid[16];
 438         uint64_t o_ctrans;
 439         uint64_t n_ctrans;
 440         char *path = NULL;
 441         int len;
 442         char *p;
 443         int cmd = FITS_CMD_SUBVOL;
 444 
 445         ret = fits_send_stream_header(f);
 446         if (ret) {
 447                 fits_abort(f);
 448                 return (ret);
 449         }
 450 
 451         if ((ret = fits_get_uuid(f, FITS_NEW, n_uuid)) ||
 452             (ret = fits_get_ctransid(f, FITS_NEW, &n_ctrans)) ||
 453             (ret = fits_get_snapname(f, FITS_NEW, &path, &len)))
 454                 goto out;
 455         /* for now, strip the pool name */
 456         if ((p = strchr(path, '/')))
 457                 ++p;
 458         else
 459                 p = path;
 460         ret = fits_get_uuid(f, FITS_OLD, o_uuid);
 461         if (ret && ret != ENOENT)
 462                 goto out;
 463         if (ret == 0) {
 464                 ret = fits_get_ctransid(f, FITS_OLD, &o_ctrans);
 465                 if (ret)
 466                         goto out;
 467                 cmd = FITS_CMD_SNAPSHOT;
 468         }
 469         if ((ret = fits_cmd_start(f, cmd)) ||
 470             (ret = fits_send_put_attr(f, FITS_ATTR_PATH, p, strlen(p))) ||
 471             (ret = fits_send_put_u64(f, FITS_ATTR_CTRANSID, n_ctrans)) ||
 472             (ret = fits_send_put_attr(f, FITS_ATTR_UUID, n_uuid, 16)))
 473                 goto out;
 474         if (cmd == FITS_CMD_SNAPSHOT) {
 475                 if ((ret = fits_send_put_u64(f, FITS_ATTR_CLONE_CTRANSID,
 476                     o_ctrans)) ||
 477                     (ret = fits_send_put_attr(f, FITS_ATTR_CLONE_UUID,
 478                     o_uuid, 16)))
 479                         goto out;
 480         }
 481         ret = fits_cmd_send(f);
 482 
 483 out:
 484         kmem_free(path, len);
 485 
 486         return (ret);
 487 }
 488 
 489 int
 490 fits_send_create_file(fits_t *f, fits_dirent_t *dirent, uint64_t ino,
 491     int devise_tempname, fits_path_t **path_ret)
 492 {
 493         fits_path_t *path = NULL;
 494         fits_info_t si;
 495         int ret;
 496         int send_rdev = 0;
 497         int cmd;
 498         uint64_t rdev = 0;
 499         char *symlink = NULL;
 500         int symlen = 0;
 501 
 502         ret = build_path(f, dirent, ino, devise_tempname, FITS_NEW, &path);
 503         if (ret)
 504                 goto out;
 505 
 506         ret = fits_get_info(f, ino, FITS_NEW, &si,
 507                             FI_ATTR_MODE | FI_ATTR_UID | FI_ATTR_GID);
 508         if (ret)
 509                 goto out;
 510 
 511         if (S_ISREG(si.si_mode)) {
 512                 cmd = FITS_CMD_MKFILE;
 513         } else if (S_ISDIR(si.si_mode)) {
 514                 cmd = FITS_CMD_MKDIR;
 515         } else if (S_ISLNK(si.si_mode)) {
 516                 cmd = FITS_CMD_SYMLINK;
 517                 ret = fits_read_symlink(f, ino, FITS_NEW, &symlink, &symlen);
 518                 if (ret)
 519                         goto out;
 520         } else if (S_ISCHR(si.si_mode) || S_ISBLK(si.si_mode)) {
 521                 cmd = FITS_CMD_MKNOD;
 522                 send_rdev = 1;
 523         } else if (S_ISFIFO(si.si_mode)) {
 524                 cmd = FITS_CMD_MKFIFO;
 525         } else if (S_ISSOCK(si.si_mode)) {
 526                 cmd = FITS_CMD_MKSOCK;
 527         } else {
 528                 /* unknown file type, ignore for now */
 529                 printf("!send_create_file: ignore file with mode 0%lo\n",
 530                         (unsigned long)si.si_mode);
 531                 return (0);
 532         }
 533 
 534         if (send_rdev) {
 535                 fits_info_t sirdev;
 536                 uint64_t r_major;
 537                 uint64_t r_minor;
 538                 ret = fits_get_info(f, ino, FITS_NEW, &sirdev, FI_ATTR_RDEV);
 539                 if (ret)
 540                         goto out;
 541                 rdev = sirdev.si_rdev;
 542 
 543                 /* XXX hardcodedly transform rdev to linux form */
 544                 r_major = rdev >> 32;
 545                 r_minor = rdev & 0xffffffful;
 546                 rdev = ((r_minor & 0xff) | ((r_major & 0xfff) << 8) |
 547                     ((r_minor >> 8) << 20) | ((r_major >> 12) << 44));
 548         }
 549         /* send MKFILE */
 550         if ((ret = fits_cmd_start(f, cmd)) ||
 551             (ret = put_path(f, FITS_ATTR_PATH, path)) ||
 552             (ret = fits_send_put_u64(f, FITS_ATTR_INO, ino)))
 553                 goto out;
 554         if (send_rdev) {
 555                 ret = fits_send_put_u64(f, FITS_ATTR_RDEV, rdev);
 556                 if (ret)
 557                         goto out;
 558                 ret = fits_send_put_u64(f, FITS_ATTR_MODE, si.si_mode);
 559                 if (ret)
 560                         goto out;
 561         }
 562         if (S_ISLNK(si.si_mode)) {
 563                 ret = fits_send_put_attr(f, FITS_ATTR_PATH_LINK,
 564                                         symlink, strlen(symlink));
 565                 if (ret)
 566                         goto out;
 567         }
 568         if ((ret = fits_cmd_send(f)))
 569                 goto out;
 570 
 571         /* send CHOWN */
 572         if ((ret = fits_cmd_start(f, FITS_CMD_CHOWN)) ||
 573             (ret = put_path(f, FITS_ATTR_PATH, path)) ||
 574             (ret = fits_send_put_u64(f, FITS_ATTR_UID, si.si_uid)) ||
 575             (ret = fits_send_put_u64(f, FITS_ATTR_GID, si.si_gid)) ||
 576             (ret = fits_cmd_send(f)))
 577                 goto out;
 578 
 579         /* send CHMOD */
 580         if ((ret = fits_cmd_start(f, FITS_CMD_CHMOD)) ||
 581             (ret = put_path(f, FITS_ATTR_PATH, path)) ||
 582             (ret = fits_send_put_u64(f, FITS_ATTR_MODE,
 583             si.si_mode & 0xfff)) ||
 584             (ret = fits_cmd_send(f)))
 585                 goto out;
 586 
 587 out:
 588         if (ret == 0 && path_ret)
 589                 *path_ret = path;
 590         else
 591                 fits_path_free(path);
 592         if (symlink)
 593                 kmem_free(symlink, symlen);
 594         return (ret);
 595 }
 596 
 597 int
 598 fits_send_link(fits_t *f, fits_dirent_t *new_dirent, uint64_t ino,
 599     uint64_t old_parent_ino, fits_which_t which, int devise_tempname)
 600 {
 601         fits_path_t *new_path = NULL;
 602         fits_path_t *old_path = NULL;
 603         int ret;
 604         fits_dirent_t old_dirent = {
 605                 .fd_name = NULL,
 606                 .fd_parent_ino = old_parent_ino,
 607                 .fd_prev = NULL,
 608         };
 609 
 610         ret = fits_find_entry(f, old_parent_ino, ino, which,
 611             &old_dirent.fd_name);
 612         if (ret)
 613                 return (ret);
 614         ret = build_path(f, &old_dirent, ino, 0, FITS_OLD, &old_path);
 615         if (ret)
 616                 goto out;
 617 
 618         ret = build_path(f, new_dirent, ino, devise_tempname, FITS_NEW,
 619             &new_path);
 620         if (ret)
 621                 goto out;
 622 
 623         if ((ret = fits_cmd_start(f, FITS_CMD_LINK)) ||
 624             (ret = put_path(f, FITS_ATTR_PATH_LINK, old_path)) ||
 625             (ret = put_path(f, FITS_ATTR_PATH, new_path)) ||
 626             (ret = fits_cmd_send(f)))
 627                 goto out;
 628 
 629         if (f->f_pass == PASS_UNLINK)
 630                 ret = fits_send_mtime_update(f, new_dirent, ino);
 631 out:
 632         if (old_dirent.fd_name)
 633                 kmem_free(old_dirent.fd_name, strlen(old_dirent.fd_name) + 1);
 634         fits_path_free(old_path);
 635         fits_path_free(new_path);
 636         return (ret);
 637 }
 638 
 639 int
 640 fits_send_mkdir(fits_t *f, fits_dirent_t *dirent,
 641                 uint64_t ino, int devise_tempname)
 642 {
 643         fits_path_t *path = NULL;
 644         fits_info_t si;
 645         int ret;
 646 
 647         ret = build_path(f, dirent, ino, devise_tempname, FITS_NEW, &path);
 648         if (ret)
 649                 goto out;
 650 
 651         ret = fits_get_info(f, ino, FITS_NEW, &si,
 652                             FI_ATTR_UID | FI_ATTR_GID | FI_ATTR_MODE);
 653         if (ret)
 654                 goto out;
 655 
 656         if (path->fp_total_len != 1) {
 657                 /* don't send an mkdir for the root, but send chown/chmod */
 658                 if ((ret = fits_cmd_start(f, FITS_CMD_MKDIR)) ||
 659                     (ret = put_path(f, FITS_ATTR_PATH, path)) ||
 660                     (ret = fits_send_put_u64(f, FITS_ATTR_INO, ino)) ||
 661                     (ret = fits_cmd_send(f)))
 662                         goto out;
 663         }
 664 
 665         /* send CHOWN */
 666         if ((ret = fits_cmd_start(f, FITS_CMD_CHOWN)) ||
 667             (ret = put_path(f, FITS_ATTR_PATH, path)) ||
 668             (ret = fits_send_put_u64(f, FITS_ATTR_UID, si.si_uid)) ||
 669             (ret = fits_send_put_u64(f, FITS_ATTR_GID, si.si_gid)) ||
 670             (ret = fits_cmd_send(f)))
 671                 goto out;
 672 
 673         /* send CHMOD */
 674         if ((ret = fits_cmd_start(f, FITS_CMD_CHMOD)) ||
 675             (ret = put_path(f, FITS_ATTR_PATH, path)) ||
 676             (ret = fits_send_put_u64(f, FITS_ATTR_MODE,
 677             si.si_mode & 0xfff)) ||
 678             (ret = fits_cmd_send(f)))
 679                 goto out;
 680 
 681 out:
 682         fits_path_free(path);
 683         return (ret);
 684 }
 685 
 686 /* this one is only used for directory renames */
 687 int
 688 fits_send_rename(fits_t *f, fits_dirent_t *new_dirent, uint64_t ino,
 689     uint64_t old_parent_ino, int devise_tempname)
 690 {
 691         fits_path_t *new_path = NULL;
 692         fits_path_t *old_path = NULL;
 693         int ret;
 694         fits_dirent_t old_dirent = {
 695                 .fd_name = NULL,
 696                 .fd_parent_ino = old_parent_ino,
 697                 .fd_prev = NULL,
 698         };
 699 
 700         ret = fits_find_entry(f, old_parent_ino, ino, FITS_OLD,
 701             &old_dirent.fd_name);
 702         if (ret)
 703                 return (ret);
 704         ret = build_path(f, &old_dirent, ino, 0, FITS_OLD, &old_path);
 705         if (ret)
 706                 goto out;
 707 
 708         ret = build_path(f, new_dirent, ino, devise_tempname, FITS_NEW,
 709             &new_path);
 710         if (ret)
 711                 goto out;
 712 
 713         if ((ret = fits_cmd_start(f, FITS_CMD_RENAME)) ||
 714             (ret = put_path(f, FITS_ATTR_PATH, old_path)) ||
 715             (ret = put_path(f, FITS_ATTR_PATH_TO, new_path)) ||
 716             (ret = fits_cmd_send(f)))
 717                 goto out;
 718 out:
 719         fits_path_free(old_path);
 720         fits_path_free(new_path);
 721         return (ret);
 722 }
 723 
 724 int
 725 fits_send_rename_from_tempname(fits_t *f, fits_dirent_t *dirent,
 726     uint64_t ino, uint64_t old)
 727 {
 728         char buf[TEMPNAME_SIZE];
 729         fits_path_t *new_path = NULL;
 730         fits_path_t *old_path = NULL;
 731         int ret;
 732         fits_dirent_t old_dirent;
 733 
 734         tempname(old, buf, sizeof (buf));
 735         old_dirent = *dirent;
 736         old_dirent.fd_name = buf;
 737 
 738         ret = build_path(f, &old_dirent, old, 0, FITS_OLD, &old_path);
 739         if (ret)
 740                 goto out;
 741         ret = build_path(f, dirent, ino, 0, FITS_NEW, &new_path);
 742         if (ret)
 743                 goto out;
 744 
 745         if ((ret = fits_cmd_start(f, FITS_CMD_RENAME)) ||
 746             (ret = put_path(f, FITS_ATTR_PATH, old_path)) ||
 747             (ret = put_path(f, FITS_ATTR_PATH_TO, new_path)) ||
 748             (ret = fits_cmd_send(f)))
 749                 goto out;
 750 
 751         ret = fits_send_mtime_update(f, dirent, old);
 752 
 753 out:
 754         fits_path_free(old_path);
 755         fits_path_free(new_path);
 756         return (ret);
 757 }
 758 
 759 int
 760 fits_send_unlink(fits_t *f, fits_dirent_t *dirent, uint64_t ino)
 761 {
 762         fits_path_t *path = NULL;
 763         int ret;
 764 
 765         ret = build_path(f, dirent, ino, 0, FITS_OLD, &path);
 766         if (ret)
 767                 goto out;
 768 
 769         if ((ret = fits_cmd_start(f, FITS_CMD_UNLINK)) ||
 770             (ret = put_path(f, FITS_ATTR_PATH, path)) ||
 771             (ret = fits_cmd_send(f)))
 772                 goto out;
 773 
 774 out:
 775         fits_path_free(path);
 776         return (ret);
 777 }
 778 
 779 int
 780 fits_send_rmdir(fits_t *f, fits_dirent_t *dirent, uint64_t ino)
 781 {
 782         fits_path_t *path;
 783         int ret;
 784 
 785         ret = build_path(f, dirent, ino, 0, FITS_OLD, &path);
 786         if (ret)
 787                 goto out;
 788 
 789         if ((ret = fits_cmd_start(f, FITS_CMD_RMDIR)) ||
 790             (ret = put_path(f, FITS_ATTR_PATH, path)) ||
 791             (ret = fits_cmd_send(f)))
 792                 goto out;
 793 
 794 out:
 795         fits_path_free(path);
 796         return (ret);
 797 }
 798 
 799 int
 800 fits_send_file_data(fits_t *f, fits_path_t **path_p,
 801                     fits_dirent_t *dirent, uint64_t ino,
 802                     uint64_t off, uint64_t len, void *data)
 803 {
 804         int ret = 0;
 805 
 806         if (!*path_p) {
 807                 ret = build_path(f, dirent, ino, 0, FITS_NEW, path_p);
 808                 if (ret)
 809                         return (ret);
 810         }
 811 
 812         while (len) {
 813                 uint64_t l = MIN(len, FITS_SEND_READ_SIZE);
 814 
 815                 if ((ret = fits_cmd_start(f, FITS_CMD_WRITE)) ||
 816                     (ret = put_path(f, FITS_ATTR_PATH, *path_p)) ||
 817                     (ret = fits_send_put_u64(f, FITS_ATTR_FILE_OFFSET, off))||
 818                     (ret = fits_send_put_attr(f, FITS_ATTR_DATA, data, l)) ||
 819                     (ret = fits_cmd_send(f)))
 820                         goto out;
 821                 data += l;
 822                 off += l;
 823                 len -= l;
 824         }
 825 
 826 out:
 827         return (ret);
 828 }
 829 
 830 int
 831 fits_send_mtime_update(fits_t *f, fits_dirent_t *dirent, uint64_t ino)
 832 {
 833         fits_path_t *path = NULL;
 834         int ret;
 835         fits_info_t si;
 836 
 837         ret = fits_get_info(f, ino, FITS_NEW, &si,
 838                             FI_ATTR_ATIME | FI_ATTR_MTIME |
 839                             FI_ATTR_CTIME | FI_ATTR_OTIME);
 840         if (ret) {
 841                 if (ret == ENOENT)
 842                         ret = 0;
 843                 goto out;
 844         }
 845 
 846         ret = build_path(f, dirent, ino, 0, FITS_NEW, &path);
 847         if (ret)
 848                 goto out;
 849 
 850         if ((ret = fits_cmd_start(f, FITS_CMD_UTIMES)) ||
 851             (ret = put_path(f, FITS_ATTR_PATH, path)) ||
 852             (ret = fits_send_put_time(f, FITS_ATTR_ATIME, &si.si_atime)) ||
 853             (ret = fits_send_put_time(f, FITS_ATTR_MTIME, &si.si_mtime)) ||
 854             (ret = fits_send_put_time(f, FITS_ATTR_CTIME, &si.si_ctime)) ||
 855             (ret = fits_send_put_time(f, FITS_ATTR_OTIME, &si.si_otime)) ||
 856             (ret = fits_cmd_send(f)))
 857                 goto out;
 858 
 859 out:
 860         fits_path_free(path);
 861         return (ret);
 862 }
 863 
 864 int
 865 fits_send_truncate(fits_t *f, fits_dirent_t *dirent, uint64_t ino,
 866     uint64_t new_size)
 867 {
 868         fits_path_t *path = NULL;
 869         int ret;
 870 
 871         ret = build_path(f, dirent, ino, 0, FITS_NEW, &path);
 872         if (ret)
 873                 return (ret);
 874 
 875         if ((ret = fits_cmd_start(f, FITS_CMD_TRUNCATE)) ||
 876             (ret = put_path(f, FITS_ATTR_PATH, path)) ||
 877             (ret = fits_send_put_u64(f, FITS_ATTR_SIZE, new_size)) ||
 878             (ret = fits_cmd_send(f)))
 879                 goto out;
 880 
 881 out:
 882         fits_path_free(path);
 883         return (ret);
 884 }
 885 
 886 int
 887 fits_send_chown(fits_t *f, fits_dirent_t *dirent, uint64_t ino,
 888                     uint64_t new_uid, uint64_t new_gid)
 889 {
 890         fits_path_t *path = NULL;
 891         int ret;
 892 
 893         ret = build_path(f, dirent, ino, 0, FITS_NEW, &path);
 894         if (ret)
 895                 return (ret);
 896 
 897         if ((ret = fits_cmd_start(f, FITS_CMD_CHOWN)) ||
 898             (ret = put_path(f, FITS_ATTR_PATH, path)) ||
 899             (ret = fits_send_put_u64(f, FITS_ATTR_UID, new_uid)) ||
 900             (ret = fits_send_put_u64(f, FITS_ATTR_GID, new_gid)) ||
 901             (ret = fits_cmd_send(f)))
 902                 goto out;
 903 
 904 out:
 905         fits_path_free(path);
 906         return (ret);
 907 }
 908 
 909 int
 910 fits_send_chmod(fits_t *f, fits_dirent_t *dirent, uint64_t ino,
 911                     uint64_t new_mode)
 912 {
 913         fits_path_t *path = NULL;
 914         int ret;
 915 
 916         ret = build_path(f, dirent, ino, 0, FITS_NEW, &path);
 917         if (ret)
 918                 return (ret);
 919 
 920         if ((ret = fits_cmd_start(f, FITS_CMD_CHMOD)) ||
 921             (ret = put_path(f, FITS_ATTR_PATH, path)) ||
 922             (ret = fits_send_put_u64(f, FITS_ATTR_MODE, new_mode)) ||
 923             (ret = fits_cmd_send(f)))
 924                 goto out;
 925 
 926 out:
 927         fits_path_free(path);
 928         return (ret);
 929 }
 930 
 931 int
 932 fits_send_end(fits_t *f)
 933 {
 934         int ret;
 935 
 936         if ((ret = fits_cmd_start(f, FITS_CMD_END)) ||
 937             (ret = fits_cmd_send(f)))
 938                 goto out;
 939 
 940 out:
 941         return (ret);
 942 }