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