1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2012 STRATO AG. All rights reserved. 23 */ 24 #include <sys/zfs_context.h> 25 #include <sys/errno.h> 26 #include <sys/stat.h> 27 #include <sys/fits.h> 28 #include <sys/fits_impl.h> 29 30 struct fits_enum { 31 fits_t *fe_fits; 32 uint64_t fe_parent_ino; 33 fits_dirent_t *fe_dirent_chain; 34 }; 35 36 struct fits_file { 37 fits_t *ff_fits; 38 uint64_t ff_len; 39 uint64_t ff_last_byte; 40 uint64_t ff_ino; 41 fits_path_t *ff_path; 42 fits_dirent_t *ff_dirent; 43 }; 44 45 static int fits_file_data_pass1(void *fits_filep, void *data, uint64_t off, 46 uint64_t len); 47 static int fits_dirent_add_pass1(void *fits_enump, char *name, uint64_t ino); 48 static int fits_dirent_mod_pass1(void *fits_enump, char *name, 49 uint64_t ino_old, uint64_t ino_new); 50 static int fits_dir_add_pass1(fits_t *f, uint64_t ino); 51 static int fits_mod_pass1(fits_t *f, uint64_t ino); 52 53 static fits_ops_t _ops = { 54 .fits_dir_add = fits_dir_add_pass1, 55 .fits_dir_mod = fits_mod_pass1, 56 .fits_dirent_add = fits_dirent_add_pass1, 57 .fits_dirent_mod = fits_dirent_mod_pass1, 58 .fits_file_mod = fits_mod_pass1, 59 .fits_file_data = fits_file_data_pass1 60 }; 61 62 static int fits_file_add_genchange(fits_t *f, uint64_t ino); 63 64 int 65 fits_start(fits_t *f, fits_ops_t **ops) 66 { 67 int ret; 68 69 f->f_pass = PASS_LINK; 70 f->f_link_add_cnt.fc_head = NULL; 71 f->f_link_add_cnt.fc_name = "link_add_cnt"; 72 f->f_del_dir_cnt.fc_head = NULL; 73 f->f_del_dir_cnt.fc_name = "del_dir_cnt"; 74 f->f_put_back_cnt.fc_head = NULL; 75 f->f_put_back_cnt.fc_name = "put_back_cnt"; 76 fits_send_init(f); 77 78 *ops = &_ops; 79 80 ret = fits_send_start(f); 81 if (ret) { 82 fits_abort(f); 83 return (ret); 84 } 85 86 return (0); 87 } 88 89 static int 90 enum_dir(fits_t *f, uint64_t ino, fits_dirent_t *chain) 91 { 92 struct fits_enum fe = { 93 .fe_fits = f, 94 .fe_parent_ino = ino, 95 .fe_dirent_chain = chain 96 }; 97 98 return (fits_dir_contents(f, ino, &fe)); 99 } 100 101 static int 102 fits_file_data_pass1(void *fits_filep, void *data, uint64_t off, uint64_t len) 103 { 104 struct fits_file *ff = fits_filep; 105 106 if (off + len > ff->ff_len) 107 len = ff->ff_len - off; 108 109 ff->ff_last_byte = off + len; 110 111 return fits_send_file_data(ff->ff_fits, &ff->ff_path, 112 ff->ff_dirent, ff->ff_ino, off, len, data); 113 } 114 115 static int 116 dirent_add_dir(fits_t *f, fits_dirent_t *dirent, uint64_t ino, int exists) 117 { 118 fits_info_t si_old; 119 fits_info_t si_new; 120 int ret; 121 122 ret = fits_get_info(f, ino, FITS_OLD, &si_old, 123 FI_ATTR_PARENT | FI_ATTR_GEN | FI_ATTR_MODE); 124 if (ret && ret != ENOENT) 125 return (ret); 126 127 if (ret == 0) { 128 ret = fits_get_info(f, ino, FITS_NEW, &si_new, 129 FI_ATTR_GEN | FI_ATTR_MODE); 130 if (ret) 131 return (ret); 132 133 if (si_old.si_gen == si_new.si_gen || 134 (S_ISDIR(si_new.si_mode) && S_ISDIR(si_old.si_mode))) 135 return fits_send_rename(f, dirent, ino, 136 si_old.si_parent, exists); 137 return (0); 138 } 139 140 if (ino > f->f_current_ino) 141 return (0); 142 143 /* dir is new */ 144 ret = fits_send_mkdir(f, dirent, ino, exists); 145 if (ret) 146 return (ret); 147 148 return (enum_dir(f, ino, dirent)); 149 } 150 151 int 152 fits_dirent_add_file(fits_t *f, fits_dirent_t *dirent, 153 uint64_t ino, uint64_t mode, int exists) 154 { 155 fits_info_t si_old; 156 fits_info_t si_new; 157 int ret; 158 fits_path_t *fits_path; 159 uint64_t new_count; 160 uint64_t old_aux; 161 162 ret = fits_get_info(f, ino, FITS_OLD, &si_old, 163 FI_ATTR_GEN | FI_ATTR_PARENT); 164 if (ret && ret != ENOENT) 165 return (ret); 166 167 if (ret == 0) { 168 ret = fits_get_info(f, ino, FITS_NEW, &si_new, 169 FI_ATTR_GEN); 170 if (ret) 171 return (ret); 172 173 if (si_old.si_gen == si_new.si_gen) 174 return fits_send_link(f, dirent, ino, si_old.si_parent, 175 FITS_OLD, exists); 176 } 177 178 /* file is new */ 179 ret = fits_add_count(&f->f_link_add_cnt, ino, 1, dirent->fd_parent_ino, 180 &new_count, &old_aux); 181 if (ret) 182 return (ret); 183 184 if (new_count == 1) 185 ret = fits_send_create_file(f, dirent, ino, 186 exists, &fits_path); 187 else 188 ret = fits_send_link(f, dirent, ino, old_aux, FITS_NEW, exists); 189 if (ret) 190 return (ret); 191 192 ret = fits_get_info(f, ino, FITS_NEW, &si_new, 193 FI_ATTR_SIZE | FI_ATTR_LINKS); 194 ASSERT(ret == 0); 195 if (new_count == 1 && S_ISREG(mode)) { 196 struct fits_file ff; 197 198 ff.ff_ino = ino; 199 ff.ff_len = si_new.si_size; 200 ff.ff_fits = f; 201 ff.ff_path = fits_path; 202 ff.ff_dirent = dirent; 203 ff.ff_last_byte = 0; 204 ret = fits_file_contents(f, ino, &ff); 205 fits_path_free(ff.ff_path); 206 if (ret) 207 return (ret); 208 if (ff.ff_last_byte != si_new.si_size) { 209 /* sparse end */ 210 ret = fits_send_truncate(f, NULL, ino, si_new.si_size); 211 if (ret) 212 return (ret); 213 } 214 } 215 if (new_count == si_new.si_nlinks) 216 fits_free_count(&f->f_link_add_cnt, ino); 217 218 return (0); 219 } 220 221 static int 222 dirent_add(fits_t *f, fits_dirent_t *dirent, uint64_t ino, int exists) 223 { 224 fits_info_t si; 225 int ret; 226 227 ret = fits_get_info(f, ino, FITS_NEW, &si, FI_ATTR_MODE); 228 if (ret) 229 return (ret); 230 231 if (S_ISDIR(si.si_mode)) { 232 return (dirent_add_dir(f, dirent, ino, exists)); 233 } else { 234 return (fits_dirent_add_file(f, dirent, ino, si.si_mode, 235 exists)); 236 } 237 } 238 239 static int 240 fits_dirent_add_pass1(void *fits_enump, char *name, uint64_t ino) 241 { 242 struct fits_enum *fe = fits_enump; 243 fits_dirent_t dirent = { 244 .fd_name = name, 245 .fd_parent_ino = fe->fe_parent_ino, 246 .fd_prev = fe->fe_dirent_chain, 247 }; 248 249 return (dirent_add(fe->fe_fits, &dirent, ino, 0)); 250 } 251 252 static int 253 fits_dirent_mod_pass1(void *fits_enump, char *name, 254 uint64_t ino_old, uint64_t ino_new) 255 { 256 struct fits_enum *fe = fits_enump; 257 fits_dirent_t dirent = { 258 .fd_name = name, 259 .fd_parent_ino = fe->fe_parent_ino, 260 .fd_prev = fe->fe_dirent_chain, 261 }; 262 263 return (dirent_add(fe->fe_fits, &dirent, ino_new, 1)); 264 } 265 266 static int 267 fits_file_add_genchange(fits_t *f, uint64_t ino) 268 { 269 int ret; 270 char *name = NULL; 271 272 f->f_current_ino = ino; 273 f->f_current_path = NULL; 274 275 /* 276 * only called when generation has changed. TODO: move to own 277 * function 278 */ 279 fits_info_t si_old; 280 fits_info_t si_new; 281 int same_name = 0; 282 283 ret = fits_get_info(f, ino, FITS_OLD, &si_old, FI_ATTR_MODE | 284 FI_ATTR_LINKS | FI_ATTR_PARENT); 285 if (ret) 286 return (ret); 287 ret = fits_get_info(f, ino, FITS_NEW, &si_new, FI_ATTR_MODE | 288 FI_ATTR_LINKS | FI_ATTR_PARENT); 289 if (ret) 290 return (ret); 291 292 if (si_old.si_nlinks > 1 && si_new.si_nlinks > 1) 293 return fits_add_count(&f->f_link_add_cnt, ino, 0, 0, NULL, 294 NULL); 295 296 if (S_ISDIR(si_old.si_mode)) 297 return (0); 298 299 fits_which_t from; 300 fits_which_t to; 301 uint64_t new_ino; 302 uint64_t parent = si_new.si_parent; 303 304 if (si_old.si_nlinks == 1) { 305 from = FITS_OLD; 306 to = FITS_NEW; 307 parent = si_old.si_parent; 308 } else if (si_old.si_nlinks > 1 && si_new.si_nlinks == 1) { 309 from = FITS_NEW; 310 to = FITS_OLD; 311 parent = si_new.si_parent; 312 } else { 313 return (EINVAL); 314 } 315 316 ret = fits_find_entry(f, parent, ino, from, &name); 317 if (ret) 318 return (ret); 319 320 ret = fits_lookup_entry(f, parent, name, to, &new_ino); 321 if (ret && ret != ENOENT) 322 goto out; 323 if (ret == 0 && new_ino == ino) 324 same_name = 1; 325 326 if ((si_old.si_nlinks == 1 || si_new.si_nlinks == 1) && !same_name) { 327 ret = 0; 328 goto out; 329 } 330 331 fits_dirent_t dirent = { 332 .fd_parent_ino = parent, 333 .fd_name = name, 334 .fd_prev = NULL 335 }; 336 337 ret = fits_send_unlink(f, &dirent, ino); 338 if (ret) 339 goto out; 340 ret = fits_dirent_add_file(f, &dirent, ino, si_new.si_mode, 0); 341 342 out: 343 fits_free_name(name); 344 return (ret); 345 } 346 347 static int 348 fits_dir_add_pass1(fits_t *f, uint64_t ino) 349 { 350 int ret; 351 uint64_t parent; 352 uint64_t first_parent = FITS_NO_INO; 353 fits_info_t si; 354 fits_dirent_t dirent; 355 int same_name = 0; 356 char *name = NULL; 357 358 f->f_current_ino = ino; 359 f->f_current_path = NULL; 360 361 parent = ino; 362 while (1) { 363 /* the new parent must exist, otherwise the fs is wrong */ 364 ret = fits_get_info(f, parent, FITS_NEW, &si, 365 FI_ATTR_PARENT); 366 if (ret) 367 return (ret); 368 if (first_parent == FITS_NO_INO) 369 first_parent = si.si_parent; 370 371 ret = fits_get_info(f, parent, FITS_OLD, &si, 0); 372 if (ret && ret != ENOENT) 373 return (ret); 374 if (ret != ENOENT) 375 break; 376 377 /* 378 * this check is only needed for a full send, on all 379 * incrementals the parent already exists and it breaks out 380 * above 381 */ 382 if (parent == si.si_parent) { 383 first_parent = FITS_NO_INO; 384 break; 385 } 386 parent = si.si_parent; 387 388 if (parent > ino) 389 return (0); 390 } 391 392 /* 393 * check for same-name 394 */ 395 if (first_parent != FITS_NO_INO) { 396 ret = fits_get_info(f, first_parent, FITS_OLD, &si, 397 FI_ATTR_MODE); 398 if (ret && ret != ENOENT) 399 return (ret); 400 if (ret == 0 && S_ISDIR(si.si_mode)) { 401 uint64_t old_ino; 402 403 ret = fits_find_entry(f, first_parent, ino, 404 FITS_NEW, &name); 405 if (ret) 406 return (ret); 407 408 ret = fits_lookup_entry(f, first_parent, name, 409 FITS_OLD, &old_ino); 410 if (ret && ret != ENOENT) { 411 goto out; 412 } 413 if (ret == 0) { 414 same_name = 1; 415 dirent.fd_name = name; 416 dirent.fd_parent_ino = first_parent; 417 dirent.fd_prev = NULL; 418 if (old_ino == ino) { 419 ret = fits_add_count(&f->f_link_add_cnt, 420 ino, 0, 0, NULL, NULL); 421 if (ret) 422 goto out; 423 } 424 } 425 } 426 } 427 /* dir is new */ 428 ret = fits_send_mkdir(f, same_name ? &dirent : NULL, ino, same_name); 429 if (ret) 430 goto out; 431 432 ret = enum_dir(f, ino, NULL); 433 434 out: 435 fits_free_name(name); 436 return (ret); 437 } 438 439 static int 440 fits_mod_pass1(fits_t *f, uint64_t ino) 441 { 442 fits_info_t si_old; 443 fits_info_t si_new; 444 int ret; 445 446 f->f_current_ino = ino; 447 f->f_current_path = NULL; 448 449 ret = fits_get_info(f, ino, FITS_NEW, &si_new, FI_ATTR_SIZE | 450 FI_ATTR_MODE | FI_ATTR_GEN | FI_ATTR_UID | 451 FI_ATTR_GID | FI_ATTR_SIZE); 452 if (ret) 453 return (ret); 454 ret = fits_get_info(f, ino, FITS_OLD, &si_old, FI_ATTR_GEN | 455 FI_ATTR_MODE | FI_ATTR_UID | 456 FI_ATTR_GID | FI_ATTR_SIZE); 457 if (ret) 458 return (ret); 459 460 if (!(S_ISDIR(si_old.si_mode) && S_ISDIR(si_new.si_mode)) && 461 si_new.si_gen != si_old.si_gen) { 462 if (S_ISDIR(si_new.si_mode)) 463 return (fits_dir_add_pass1(f, ino)); 464 else 465 return (fits_file_add_genchange(f, ino)); 466 } 467 468 if (S_ISDIR(si_new.si_mode)) { 469 ret = enum_dir(f, ino, NULL); 470 if (ret) 471 return (ret); 472 } 473 474 if (S_ISREG(si_new.si_mode)) { 475 struct fits_file ff; 476 ff.ff_ino = ino; 477 ff.ff_len = si_new.si_size; 478 ff.ff_fits = f; 479 ff.ff_path = NULL; 480 ff.ff_dirent = NULL; 481 ff.ff_last_byte = 0; 482 483 ret = fits_file_contents(f, ino, &ff); 484 fits_path_free(ff.ff_path); 485 if (ret) 486 return (ret); 487 if (si_new.si_size < si_old.si_size || 488 (si_new.si_size != si_old.si_size && 489 si_new.si_size != ff.ff_last_byte)) { 490 ret = fits_send_truncate(f, NULL, ino, si_new.si_size); 491 if (ret) 492 return (ret); 493 } 494 } 495 496 if (si_old.si_uid != si_new.si_uid || si_old.si_gid != si_new.si_gid) { 497 ret = fits_send_chown(f, NULL, ino, si_new.si_uid, 498 si_new.si_gid); 499 if (ret) 500 return (ret); 501 } 502 if (si_old.si_mode != si_new.si_mode) { 503 ret = fits_send_chmod(f, NULL, ino, si_new.si_mode); 504 if (ret) 505 return (ret); 506 } 507 508 return (ret); 509 }