1/*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2010-2012 Semihalf. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD$"); 31 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/conf.h> 35#include <sys/kernel.h> 36#include <sys/lock.h> 37#include <sys/malloc.h> 38#include <sys/mount.h> 39#include <sys/mutex.h> 40#include <sys/namei.h> 41#include <sys/sysctl.h> 42#include <sys/vnode.h> 43#include <sys/buf.h> 44#include <sys/bio.h> 45 46#include <vm/vm.h> 47#include <vm/vm_param.h> 48#include <vm/vm_kern.h> 49#include <vm/vm_page.h> 50 51#include "nandfs_mount.h" 52#include "nandfs.h" 53#include "nandfs_subr.h" 54 55 56static int 57nandfs_checkpoint_size(struct nandfs_device *fsdev) 58{ 59 60 return (fsdev->nd_fsdata.f_checkpoint_size); 61} 62 63static int 64nandfs_checkpoint_blk_offset(struct nandfs_device *fsdev, uint64_t cn, 65 uint64_t *blk, uint64_t *offset) 66{ 67 uint64_t off; 68 uint16_t cp_size, cp_per_blk; 69 70 KASSERT((cn), ("checkpoing cannot be zero")); 71 72 cp_size = fsdev->nd_fsdata.f_checkpoint_size; 73 cp_per_blk = fsdev->nd_blocksize / cp_size; 74 off = roundup(sizeof(struct nandfs_cpfile_header), cp_size) / cp_size; 75 off += (cn - 1); 76 77 *blk = off / cp_per_blk; 78 *offset = (off % cp_per_blk) * cp_size; 79 80 return (0); 81} 82 83static int 84nandfs_checkpoint_blk_remaining(struct nandfs_device *fsdev, uint64_t cn, 85 uint64_t blk, uint64_t offset) 86{ 87 uint16_t cp_size, cp_remaining; 88 89 cp_size = fsdev->nd_fsdata.f_checkpoint_size; 90 cp_remaining = (fsdev->nd_blocksize - offset) / cp_size; 91 92 return (cp_remaining); 93} 94 95int 96nandfs_get_checkpoint(struct nandfs_device *fsdev, struct nandfs_node *cp_node, 97 uint64_t cn) 98{ 99 struct buf *bp; 100 uint64_t blk, offset; 101 int error; 102 103 if (cn != fsdev->nd_last_cno && cn != (fsdev->nd_last_cno + 1)) { 104 return (-1); 105 } 106 107 error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp); 108 if (error) { 109 brelse(bp); 110 return (-1); 111 } 112 113 error = nandfs_dirty_buf(bp, 0); 114 if (error) 115 return (-1); 116 117 118 nandfs_checkpoint_blk_offset(fsdev, cn, &blk, &offset); 119 120 if (blk != 0) { 121 if (blk < cp_node->nn_inode.i_blocks) 122 error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp); 123 else 124 error = nandfs_bcreate(cp_node, blk, NOCRED, 0, &bp); 125 if (error) { 126 if (bp) 127 brelse(bp); 128 return (-1); 129 } 130 131 nandfs_dirty_buf(bp, 1); 132 } 133 134 DPRINTF(CPFILE, ("%s: cn:%#jx entry block:%#jx offset:%#jx\n", 135 __func__, (uintmax_t)cn, (uintmax_t)blk, (uintmax_t)offset)); 136 137 return (0); 138} 139 140int 141nandfs_set_checkpoint(struct nandfs_device *fsdev, struct nandfs_node *cp_node, 142 uint64_t cn, struct nandfs_inode *ifile_inode, uint64_t nblocks) 143{ 144 struct nandfs_cpfile_header *cnh; 145 struct nandfs_checkpoint *cnp; 146 struct buf *bp; 147 uint64_t blk, offset; 148 int error; 149 150 if (cn != fsdev->nd_last_cno && cn != (fsdev->nd_last_cno + 1)) { 151 nandfs_error("%s: trying to set invalid chekpoint %jx - %jx\n", 152 __func__, cn, fsdev->nd_last_cno); 153 return (-1); 154 } 155 156 error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp); 157 if (error) { 158 brelse(bp); 159 return error; 160 } 161 162 cnh = (struct nandfs_cpfile_header *) bp->b_data; 163 cnh->ch_ncheckpoints++; 164 165 nandfs_checkpoint_blk_offset(fsdev, cn, &blk, &offset); 166 167 if(blk != 0) { 168 brelse(bp); 169 error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp); 170 if (error) { 171 brelse(bp); 172 return error; 173 } 174 } 175 176 cnp = (struct nandfs_checkpoint *)((uint8_t *)bp->b_data + offset); 177 cnp->cp_flags = 0; 178 cnp->cp_checkpoints_count = 1; 179 memset(&cnp->cp_snapshot_list, 0, sizeof(struct nandfs_snapshot_list)); 180 cnp->cp_cno = cn; 181 cnp->cp_create = fsdev->nd_ts.tv_sec; 182 cnp->cp_nblk_inc = nblocks; 183 cnp->cp_blocks_count = 0; 184 memcpy (&cnp->cp_ifile_inode, ifile_inode, sizeof(cnp->cp_ifile_inode)); 185 186 DPRINTF(CPFILE, ("%s: cn:%#jx ctime:%#jx nblk:%#jx\n", 187 __func__, (uintmax_t)cn, (uintmax_t)cnp->cp_create, 188 (uintmax_t)nblocks)); 189 190 brelse(bp); 191 return (0); 192} 193 194static int 195nandfs_cp_mounted(struct nandfs_device *nandfsdev, uint64_t cno) 196{ 197 struct nandfsmount *nmp; 198 int mounted = 0; 199 200 mtx_lock(&nandfsdev->nd_mutex); 201 /* No double-mounting of the same checkpoint */ 202 STAILQ_FOREACH(nmp, &nandfsdev->nd_mounts, nm_next_mount) { 203 if (nmp->nm_mount_args.cpno == cno) { 204 mounted = 1; 205 break; 206 } 207 } 208 mtx_unlock(&nandfsdev->nd_mutex); 209 210 return (mounted); 211} 212 213static int 214nandfs_cp_set_snapshot(struct nandfs_node *cp_node, uint64_t cno) 215{ 216 struct nandfs_device *fsdev; 217 struct nandfs_cpfile_header *cnh; 218 struct nandfs_checkpoint *cnp; 219 struct nandfs_snapshot_list *list; 220 struct buf *bp; 221 uint64_t blk, prev_blk, offset; 222 uint64_t curr, prev; 223 int error; 224 225 fsdev = cp_node->nn_nandfsdev; 226 227 /* Get snapshot data */ 228 nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset); 229 error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp); 230 if (error) { 231 brelse(bp); 232 return (error); 233 } 234 cnp = (struct nandfs_checkpoint *)(bp->b_data + offset); 235 if (cnp->cp_flags & NANDFS_CHECKPOINT_INVALID) { 236 brelse(bp); 237 return (ENOENT); 238 } 239 if ((cnp->cp_flags & NANDFS_CHECKPOINT_SNAPSHOT)) { 240 brelse(bp); 241 return (EINVAL); 242 } 243 244 brelse(bp); 245 /* Get list from header */ 246 error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp); 247 if (error) { 248 brelse(bp); 249 return (error); 250 } 251 252 cnh = (struct nandfs_cpfile_header *) bp->b_data; 253 list = &cnh->ch_snapshot_list; 254 prev = list->ssl_prev; 255 brelse(bp); 256 prev_blk = ~(0); 257 curr = 0; 258 while (prev > cno) { 259 curr = prev; 260 nandfs_checkpoint_blk_offset(fsdev, prev, &prev_blk, &offset); 261 error = nandfs_bread(cp_node, prev_blk, NOCRED, 0, &bp); 262 if (error) { 263 brelse(bp); 264 return (error); 265 } 266 cnp = (struct nandfs_checkpoint *)(bp->b_data + offset); 267 list = &cnp->cp_snapshot_list; 268 prev = list->ssl_prev; 269 brelse(bp); 270 } 271 272 if (curr == 0) { 273 nandfs_bread(cp_node, 0, NOCRED, 0, &bp); 274 cnh = (struct nandfs_cpfile_header *) bp->b_data; 275 list = &cnh->ch_snapshot_list; 276 } else { 277 nandfs_checkpoint_blk_offset(fsdev, curr, &blk, &offset); 278 error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp); 279 if (error) { 280 brelse(bp); 281 return (error); 282 } 283 cnp = (struct nandfs_checkpoint *)(bp->b_data + offset); 284 list = &cnp->cp_snapshot_list; 285 } 286 287 list->ssl_prev = cno; 288 error = nandfs_dirty_buf(bp, 0); 289 if (error) 290 return (error); 291 292 293 /* Update snapshot for cno */ 294 nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset); 295 error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp); 296 if (error) { 297 brelse(bp); 298 return (error); 299 } 300 cnp = (struct nandfs_checkpoint *)(bp->b_data + offset); 301 list = &cnp->cp_snapshot_list; 302 list->ssl_prev = prev; 303 list->ssl_next = curr; 304 cnp->cp_flags |= NANDFS_CHECKPOINT_SNAPSHOT; 305 nandfs_dirty_buf(bp, 1); 306 307 if (prev == 0) { 308 nandfs_bread(cp_node, 0, NOCRED, 0, &bp); 309 cnh = (struct nandfs_cpfile_header *) bp->b_data; 310 list = &cnh->ch_snapshot_list; 311 } else { 312 /* Update snapshot list for prev */ 313 nandfs_checkpoint_blk_offset(fsdev, prev, &blk, &offset); 314 error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp); 315 if (error) { 316 brelse(bp); 317 return (error); 318 } 319 cnp = (struct nandfs_checkpoint *)(bp->b_data + offset); 320 list = &cnp->cp_snapshot_list; 321 } 322 list->ssl_next = cno; 323 nandfs_dirty_buf(bp, 1); 324 325 /* Update header */ 326 error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp); 327 if (error) { 328 brelse(bp); 329 return (error); 330 } 331 cnh = (struct nandfs_cpfile_header *) bp->b_data; 332 cnh->ch_nsnapshots++; 333 nandfs_dirty_buf(bp, 1); 334 335 return (0); 336} 337 338static int 339nandfs_cp_clr_snapshot(struct nandfs_node *cp_node, uint64_t cno) 340{ 341 struct nandfs_device *fsdev; 342 struct nandfs_cpfile_header *cnh; 343 struct nandfs_checkpoint *cnp; 344 struct nandfs_snapshot_list *list; 345 struct buf *bp; 346 uint64_t blk, offset, snapshot_cnt; 347 uint64_t next, prev; 348 int error; 349 350 fsdev = cp_node->nn_nandfsdev; 351 352 /* Get snapshot data */ 353 nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset); 354 error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp); 355 if (error) { 356 brelse(bp); 357 return (error); 358 } 359 cnp = (struct nandfs_checkpoint *)(bp->b_data + offset); 360 if (cnp->cp_flags & NANDFS_CHECKPOINT_INVALID) { 361 brelse(bp); 362 return (ENOENT); 363 } 364 if (!(cnp->cp_flags & NANDFS_CHECKPOINT_SNAPSHOT)) { 365 brelse(bp); 366 return (EINVAL); 367 } 368 369 list = &cnp->cp_snapshot_list; 370 next = list->ssl_next; 371 prev = list->ssl_prev; 372 brelse(bp); 373 374 /* Get previous snapshot */ 375 if (prev != 0) { 376 nandfs_checkpoint_blk_offset(fsdev, prev, &blk, &offset); 377 error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp); 378 if (error) { 379 brelse(bp); 380 return (error); 381 } 382 cnp = (struct nandfs_checkpoint *)(bp->b_data + offset); 383 list = &cnp->cp_snapshot_list; 384 } else { 385 nandfs_bread(cp_node, 0, NOCRED, 0, &bp); 386 cnh = (struct nandfs_cpfile_header *) bp->b_data; 387 list = &cnh->ch_snapshot_list; 388 } 389 390 list->ssl_next = next; 391 error = nandfs_dirty_buf(bp, 0); 392 if (error) 393 return (error); 394 395 /* Get next snapshot */ 396 if (next != 0) { 397 nandfs_checkpoint_blk_offset(fsdev, next, &blk, &offset); 398 error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp); 399 if (error) { 400 brelse(bp); 401 return (error); 402 } 403 cnp = (struct nandfs_checkpoint *)(bp->b_data + offset); 404 list = &cnp->cp_snapshot_list; 405 } else { 406 nandfs_bread(cp_node, 0, NOCRED, 0, &bp); 407 cnh = (struct nandfs_cpfile_header *) bp->b_data; 408 list = &cnh->ch_snapshot_list; 409 } 410 list->ssl_prev = prev; 411 nandfs_dirty_buf(bp, 1); 412 413 /* Update snapshot list for cno */ 414 nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset); 415 error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp); 416 if (error) { 417 brelse(bp); 418 return (error); 419 } 420 cnp = (struct nandfs_checkpoint *)(bp->b_data + offset); 421 list = &cnp->cp_snapshot_list; 422 list->ssl_prev = 0; 423 list->ssl_next = 0; 424 cnp->cp_flags &= !NANDFS_CHECKPOINT_SNAPSHOT; 425 nandfs_dirty_buf(bp, 1); 426 427 /* Update header */ 428 error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp); 429 if (error) { 430 brelse(bp); 431 return (error); 432 } 433 cnh = (struct nandfs_cpfile_header *) bp->b_data; 434 snapshot_cnt = cnh->ch_nsnapshots; 435 snapshot_cnt--; 436 cnh->ch_nsnapshots = snapshot_cnt; 437 nandfs_dirty_buf(bp, 1); 438 439 return (0); 440} 441 442int 443nandfs_chng_cpmode(struct nandfs_node *node, struct nandfs_cpmode *ncpm) 444{ 445 struct nandfs_device *fsdev; 446 uint64_t cno = ncpm->ncpm_cno; 447 int mode = ncpm->ncpm_mode; 448 int ret; 449 450 fsdev = node->nn_nandfsdev; 451 VOP_LOCK(NTOV(node), LK_EXCLUSIVE); 452 switch (mode) { 453 case NANDFS_CHECKPOINT: 454 if (nandfs_cp_mounted(fsdev, cno)) { 455 ret = EBUSY; 456 } else 457 ret = nandfs_cp_clr_snapshot(node, cno); 458 break; 459 case NANDFS_SNAPSHOT: 460 ret = nandfs_cp_set_snapshot(node, cno); 461 break; 462 default: 463 ret = EINVAL; 464 break; 465 } 466 VOP_UNLOCK(NTOV(node), 0); 467 468 return (ret); 469} 470 471static void 472nandfs_cpinfo_fill(struct nandfs_checkpoint *cnp, struct nandfs_cpinfo *nci) 473{ 474 475 nci->nci_flags = cnp->cp_flags; 476 nci->nci_pad = 0; 477 nci->nci_cno = cnp->cp_cno; 478 nci->nci_create = cnp->cp_create; 479 nci->nci_nblk_inc = cnp->cp_nblk_inc; 480 nci->nci_blocks_count = cnp->cp_blocks_count; 481 nci->nci_next = cnp->cp_snapshot_list.ssl_next; 482 DPRINTF(CPFILE, ("%s: cn:%#jx ctime:%#jx\n", 483 __func__, (uintmax_t)cnp->cp_cno, 484 (uintmax_t)cnp->cp_create)); 485} 486 487static int 488nandfs_get_cpinfo_cp(struct nandfs_node *node, uint64_t cno, 489 struct nandfs_cpinfo *nci, uint32_t mnmembs, uint32_t *nmembs) 490{ 491 struct nandfs_device *fsdev; 492 struct buf *bp; 493 uint64_t blk, offset, last_cno, i; 494 uint16_t remaining; 495 int error; 496#ifdef INVARIANTS 497 uint64_t testblk, testoffset; 498#endif 499 500 if (cno == 0) { 501 return (ENOENT); 502 } 503 504 if (mnmembs < 1) { 505 return (EINVAL); 506 } 507 508 fsdev = node->nn_nandfsdev; 509 last_cno = fsdev->nd_last_cno; 510 DPRINTF(CPFILE, ("%s: cno:%#jx mnmembs: %#jx last:%#jx\n", __func__, 511 (uintmax_t)cno, (uintmax_t)mnmembs, 512 (uintmax_t)fsdev->nd_last_cno)); 513 514 /* 515 * do { 516 * get block 517 * read checkpoints until we hit last checkpoint, end of block or 518 * requested number 519 * } while (last read checkpoint <= last checkpoint on fs && 520 * read checkpoints < request number); 521 */ 522 *nmembs = i = 0; 523 do { 524 nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset); 525 remaining = nandfs_checkpoint_blk_remaining(fsdev, cno, 526 blk, offset); 527 error = nandfs_bread(node, blk, NOCRED, 0, &bp); 528 if (error) { 529 brelse(bp); 530 return (error); 531 } 532 533 while (cno <= last_cno && i < mnmembs && remaining) { 534#ifdef INVARIANTS 535 nandfs_checkpoint_blk_offset(fsdev, cno, &testblk, 536 &testoffset); 537 KASSERT(testblk == blk, ("testblk != blk")); 538 KASSERT(testoffset == offset, ("testoffset != offset")); 539#endif 540 DPRINTF(CPFILE, ("%s: cno %#jx\n", __func__, 541 (uintmax_t)cno)); 542 543 nandfs_cpinfo_fill((struct nandfs_checkpoint *) 544 (bp->b_data + offset), nci); 545 offset += nandfs_checkpoint_size(fsdev); 546 i++; 547 nci++; 548 cno++; 549 (*nmembs)++; 550 remaining--; 551 } 552 brelse(bp); 553 } while (cno <= last_cno && i < mnmembs); 554 555 return (0); 556} 557 558static int 559nandfs_get_cpinfo_sp(struct nandfs_node *node, uint64_t cno, 560 struct nandfs_cpinfo *nci, uint32_t mnmembs, uint32_t *nmembs) 561{ 562 struct nandfs_checkpoint *cnp; 563 struct nandfs_cpfile_header *cnh; 564 struct nandfs_device *fsdev; 565 struct buf *bp = NULL; 566 uint64_t curr = 0; 567 uint64_t blk, offset, curr_cno; 568 uint32_t flag; 569 int i, error; 570 571 if (cno == 0 || cno == ~(0)) 572 return (ENOENT); 573 574 fsdev = node->nn_nandfsdev; 575 curr_cno = cno; 576 577 if (nmembs) 578 *nmembs = 0; 579 if (curr_cno == 1) { 580 /* Get list from header */ 581 error = nandfs_bread(node, 0, NOCRED, 0, &bp); 582 if (error) { 583 brelse(bp); 584 return (error); 585 } 586 cnh = (struct nandfs_cpfile_header *) bp->b_data; 587 curr_cno = cnh->ch_snapshot_list.ssl_next; 588 brelse(bp); 589 bp = NULL; 590 591 /* No snapshots */ 592 if (curr_cno == 0) 593 return (0); 594 } 595 596 for (i = 0; i < mnmembs; i++, nci++) { 597 nandfs_checkpoint_blk_offset(fsdev, curr_cno, &blk, &offset); 598 if (i == 0 || curr != blk) { 599 if (bp) 600 brelse(bp); 601 error = nandfs_bread(node, blk, NOCRED, 0, &bp); 602 if (error) { 603 brelse(bp); 604 return (ENOENT); 605 } 606 curr = blk; 607 } 608 cnp = (struct nandfs_checkpoint *)(bp->b_data + offset); 609 flag = cnp->cp_flags; 610 if (!(flag & NANDFS_CHECKPOINT_SNAPSHOT) || 611 (flag & NANDFS_CHECKPOINT_INVALID)) 612 break; 613 614 nci->nci_flags = flag; 615 nci->nci_pad = 0; 616 nci->nci_cno = cnp->cp_cno; 617 nci->nci_create = cnp->cp_create; 618 nci->nci_nblk_inc = cnp->cp_nblk_inc; 619 nci->nci_blocks_count = cnp->cp_blocks_count; 620 nci->nci_next = cnp->cp_snapshot_list.ssl_next; 621 if (nmembs) 622 (*nmembs)++; 623 624 curr_cno = nci->nci_next; 625 if (!curr_cno) 626 break; 627 } 628 629 brelse(bp); 630 631 return (0); 632} 633 634int 635nandfs_get_cpinfo(struct nandfs_node *node, uint64_t cno, uint16_t flags, 636 struct nandfs_cpinfo *nci, uint32_t nmembs, uint32_t *nnmembs) 637{ 638 int error; 639 640 VOP_LOCK(NTOV(node), LK_EXCLUSIVE); 641 switch (flags) { 642 case NANDFS_CHECKPOINT: 643 error = nandfs_get_cpinfo_cp(node, cno, nci, nmembs, nnmembs); 644 break; 645 case NANDFS_SNAPSHOT: 646 error = nandfs_get_cpinfo_sp(node, cno, nci, nmembs, nnmembs); 647 break; 648 default: 649 error = EINVAL; 650 break; 651 } 652 VOP_UNLOCK(NTOV(node), 0); 653 654 return (error); 655} 656 657int 658nandfs_get_cpinfo_ioctl(struct nandfs_node *node, struct nandfs_argv *nargv) 659{ 660 struct nandfs_cpinfo *nci; 661 uint64_t cno = nargv->nv_index; 662 void *buf = (void *)((uintptr_t)nargv->nv_base); 663 uint16_t flags = nargv->nv_flags; 664 uint32_t nmembs = 0; 665 int error; 666 667 if (nargv->nv_nmembs > NANDFS_CPINFO_MAX) 668 return (EINVAL); 669 670 nci = malloc(sizeof(struct nandfs_cpinfo) * nargv->nv_nmembs, 671 M_NANDFSTEMP, M_WAITOK | M_ZERO); 672 673 error = nandfs_get_cpinfo(node, cno, flags, nci, nargv->nv_nmembs, &nmembs); 674 675 if (error == 0) { 676 nargv->nv_nmembs = nmembs; 677 error = copyout(nci, buf, 678 sizeof(struct nandfs_cpinfo) * nmembs); 679 } 680 681 free(nci, M_NANDFSTEMP); 682 return (error); 683} 684 685int 686nandfs_delete_cp(struct nandfs_node *node, uint64_t start, uint64_t end) 687{ 688 struct nandfs_checkpoint *cnp; 689 struct nandfs_device *fsdev; 690 struct buf *bp; 691 uint64_t cno = start, blk, offset; 692 int error; 693 694 DPRINTF(CPFILE, ("%s: delete cno %jx-%jx\n", __func__, start, end)); 695 VOP_LOCK(NTOV(node), LK_EXCLUSIVE); 696 fsdev = node->nn_nandfsdev; 697 for (cno = start; cno <= end; cno++) { 698 if (!cno) 699 continue; 700 701 nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset); 702 error = nandfs_bread(node, blk, NOCRED, 0, &bp); 703 if (error) { 704 VOP_UNLOCK(NTOV(node), 0); 705 brelse(bp); 706 return (error); 707 } 708 709 cnp = (struct nandfs_checkpoint *)(bp->b_data + offset); 710 if (cnp->cp_flags & NANDFS_CHECKPOINT_SNAPSHOT) { 711 brelse(bp); 712 VOP_UNLOCK(NTOV(node), 0); 713 return (0); 714 } 715 716 cnp->cp_flags |= NANDFS_CHECKPOINT_INVALID; 717 718 error = nandfs_dirty_buf(bp, 0); 719 if (error) 720 return (error); 721 } 722 VOP_UNLOCK(NTOV(node), 0); 723 724 return (0); 725} 726 727int 728nandfs_make_snap(struct nandfs_device *fsdev, uint64_t *cno) 729{ 730 struct nandfs_cpmode cpm; 731 int error; 732 733 *cno = cpm.ncpm_cno = fsdev->nd_last_cno; 734 cpm.ncpm_mode = NANDFS_SNAPSHOT; 735 error = nandfs_chng_cpmode(fsdev->nd_cp_node, &cpm); 736 return (error); 737} 738 739int 740nandfs_delete_snap(struct nandfs_device *fsdev, uint64_t cno) 741{ 742 struct nandfs_cpmode cpm; 743 int error; 744 745 cpm.ncpm_cno = cno; 746 cpm.ncpm_mode = NANDFS_CHECKPOINT; 747 error = nandfs_chng_cpmode(fsdev->nd_cp_node, &cpm); 748 return (error); 749} 750 751int nandfs_get_cpstat(struct nandfs_node *cp_node, struct nandfs_cpstat *ncp) 752{ 753 struct nandfs_device *fsdev; 754 struct nandfs_cpfile_header *cnh; 755 struct buf *bp; 756 int error; 757 758 VOP_LOCK(NTOV(cp_node), LK_EXCLUSIVE); 759 fsdev = cp_node->nn_nandfsdev; 760 761 /* Get header */ 762 error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp); 763 if (error) { 764 brelse(bp); 765 VOP_UNLOCK(NTOV(cp_node), 0); 766 return (error); 767 } 768 cnh = (struct nandfs_cpfile_header *) bp->b_data; 769 ncp->ncp_cno = fsdev->nd_last_cno; 770 ncp->ncp_ncps = cnh->ch_ncheckpoints; 771 ncp->ncp_nss = cnh->ch_nsnapshots; 772 DPRINTF(CPFILE, ("%s: cno:%#jx ncps:%#jx nss:%#jx\n", 773 __func__, ncp->ncp_cno, ncp->ncp_ncps, ncp->ncp_nss)); 774 brelse(bp); 775 VOP_UNLOCK(NTOV(cp_node), 0); 776 777 return (0); 778} 779