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 }