FreeBSD ZFS
The Zettabyte File System
|
00001 /* 00002 * CDDL HEADER START 00003 * 00004 * The contents of this file are subject to the terms of the 00005 * Common Development and Distribution License (the "License"). 00006 * You may not use this file except in compliance with the License. 00007 * 00008 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 00009 * or http://www.opensolaris.org/os/licensing. 00010 * See the License for the specific language governing permissions 00011 * and limitations under the License. 00012 * 00013 * When distributing Covered Code, include this CDDL HEADER in each 00014 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 00015 * If applicable, add the following below this CDDL HEADER, with the 00016 * fields enclosed by brackets "[]" replaced with your own identifying 00017 * information: Portions Copyright [yyyy] [name of copyright owner] 00018 * 00019 * CDDL HEADER END 00020 */ 00021 /* 00022 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 00023 */ 00024 00025 #include <sys/dmu.h> 00026 #include <sys/dmu_impl.h> 00027 #include <sys/dmu_tx.h> 00028 #include <sys/dbuf.h> 00029 #include <sys/dnode.h> 00030 #include <sys/zfs_context.h> 00031 #include <sys/dmu_objset.h> 00032 #include <sys/dmu_traverse.h> 00033 #include <sys/dsl_dataset.h> 00034 #include <sys/dsl_dir.h> 00035 #include <sys/dsl_pool.h> 00036 #include <sys/dsl_synctask.h> 00037 #include <sys/zfs_ioctl.h> 00038 #include <sys/zap.h> 00039 #include <sys/zio_checksum.h> 00040 #include <sys/zfs_znode.h> 00041 00042 struct diffarg { 00043 struct file *da_fp; /* file to which we are reporting */ 00044 offset_t *da_offp; 00045 int da_err; /* error that stopped diff search */ 00046 dmu_diff_record_t da_ddr; 00047 kthread_t *da_td; 00048 }; 00049 00050 static int 00051 write_bytes(struct diffarg *da) 00052 { 00053 struct uio auio; 00054 struct iovec aiov; 00055 00056 aiov.iov_base = (caddr_t)&da->da_ddr; 00057 aiov.iov_len = sizeof (da->da_ddr); 00058 auio.uio_iov = &aiov; 00059 auio.uio_iovcnt = 1; 00060 auio.uio_resid = aiov.iov_len; 00061 auio.uio_segflg = UIO_SYSSPACE; 00062 auio.uio_rw = UIO_WRITE; 00063 auio.uio_offset = (off_t)-1; 00064 auio.uio_td = da->da_td; 00065 #ifdef _KERNEL 00066 if (da->da_fp->f_type == DTYPE_VNODE) 00067 bwillwrite(); 00068 return (fo_write(da->da_fp, &auio, da->da_td->td_ucred, 0, da->da_td)); 00069 #else 00070 fprintf(stderr, "%s: returning EOPNOTSUPP\n", __func__); 00071 return (EOPNOTSUPP); 00072 #endif 00073 } 00074 00075 static int 00076 write_record(struct diffarg *da) 00077 { 00078 00079 if (da->da_ddr.ddr_type == DDR_NONE) { 00080 da->da_err = 0; 00081 return (0); 00082 } 00083 00084 da->da_err = write_bytes(da); 00085 *da->da_offp += sizeof (da->da_ddr); 00086 return (da->da_err); 00087 } 00088 00089 static int 00090 report_free_dnode_range(struct diffarg *da, uint64_t first, uint64_t last) 00091 { 00092 ASSERT(first <= last); 00093 if (da->da_ddr.ddr_type != DDR_FREE || 00094 first != da->da_ddr.ddr_last + 1) { 00095 if (write_record(da) != 0) 00096 return (da->da_err); 00097 da->da_ddr.ddr_type = DDR_FREE; 00098 da->da_ddr.ddr_first = first; 00099 da->da_ddr.ddr_last = last; 00100 return (0); 00101 } 00102 da->da_ddr.ddr_last = last; 00103 return (0); 00104 } 00105 00106 static int 00107 report_dnode(struct diffarg *da, uint64_t object, dnode_phys_t *dnp) 00108 { 00109 ASSERT(dnp != NULL); 00110 if (dnp->dn_type == DMU_OT_NONE) 00111 return (report_free_dnode_range(da, object, object)); 00112 00113 if (da->da_ddr.ddr_type != DDR_INUSE || 00114 object != da->da_ddr.ddr_last + 1) { 00115 if (write_record(da) != 0) 00116 return (da->da_err); 00117 da->da_ddr.ddr_type = DDR_INUSE; 00118 da->da_ddr.ddr_first = da->da_ddr.ddr_last = object; 00119 return (0); 00120 } 00121 da->da_ddr.ddr_last = object; 00122 return (0); 00123 } 00124 00125 #define DBP_SPAN(dnp, level) \ 00126 (((uint64_t)dnp->dn_datablkszsec) << (SPA_MINBLOCKSHIFT + \ 00127 (level) * (dnp->dn_indblkshift - SPA_BLKPTRSHIFT))) 00128 00129 /* ARGSUSED */ 00130 static int 00131 diff_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, arc_buf_t *pbuf, 00132 const zbookmark_t *zb, const dnode_phys_t *dnp, void *arg) 00133 { 00134 struct diffarg *da = arg; 00135 int err = 0; 00136 00137 if (issig(JUSTLOOKING) && issig(FORREAL)) 00138 return (EINTR); 00139 00140 if (zb->zb_object != DMU_META_DNODE_OBJECT) 00141 return (0); 00142 00143 if (bp == NULL) { 00144 uint64_t span = DBP_SPAN(dnp, zb->zb_level); 00145 uint64_t dnobj = (zb->zb_blkid * span) >> DNODE_SHIFT; 00146 00147 err = report_free_dnode_range(da, dnobj, 00148 dnobj + (span >> DNODE_SHIFT) - 1); 00149 if (err) 00150 return (err); 00151 } else if (zb->zb_level == 0) { 00152 dnode_phys_t *blk; 00153 arc_buf_t *abuf; 00154 uint32_t aflags = ARC_WAIT; 00155 int blksz = BP_GET_LSIZE(bp); 00156 int i; 00157 00158 if (dsl_read(NULL, spa, bp, pbuf, 00159 arc_getbuf_func, &abuf, ZIO_PRIORITY_ASYNC_READ, 00160 ZIO_FLAG_CANFAIL, &aflags, zb) != 0) 00161 return (EIO); 00162 00163 blk = abuf->b_data; 00164 for (i = 0; i < blksz >> DNODE_SHIFT; i++) { 00165 uint64_t dnobj = (zb->zb_blkid << 00166 (DNODE_BLOCK_SHIFT - DNODE_SHIFT)) + i; 00167 err = report_dnode(da, dnobj, blk+i); 00168 if (err) 00169 break; 00170 } 00171 (void) arc_buf_remove_ref(abuf, &abuf); 00172 if (err) 00173 return (err); 00174 /* Don't care about the data blocks */ 00175 return (TRAVERSE_VISIT_NO_CHILDREN); 00176 } 00177 return (0); 00178 } 00179 00180 int 00181 dmu_diff(objset_t *tosnap, objset_t *fromsnap, struct file *fp, offset_t *offp) 00182 { 00183 struct diffarg da; 00184 dsl_dataset_t *ds = tosnap->os_dsl_dataset; 00185 dsl_dataset_t *fromds = fromsnap->os_dsl_dataset; 00186 dsl_dataset_t *findds; 00187 dsl_dataset_t *relds; 00188 int err = 0; 00189 00190 /* make certain we are looking at snapshots */ 00191 if (!dsl_dataset_is_snapshot(ds) || !dsl_dataset_is_snapshot(fromds)) 00192 return (EINVAL); 00193 00194 /* fromsnap must be earlier and from the same lineage as tosnap */ 00195 if (fromds->ds_phys->ds_creation_txg >= ds->ds_phys->ds_creation_txg) 00196 return (EXDEV); 00197 00198 relds = NULL; 00199 findds = ds; 00200 00201 while (fromds->ds_dir != findds->ds_dir) { 00202 dsl_pool_t *dp = ds->ds_dir->dd_pool; 00203 00204 if (!dsl_dir_is_clone(findds->ds_dir)) { 00205 if (relds) 00206 dsl_dataset_rele(relds, FTAG); 00207 return (EXDEV); 00208 } 00209 00210 rw_enter(&dp->dp_config_rwlock, RW_READER); 00211 err = dsl_dataset_hold_obj(dp, 00212 findds->ds_dir->dd_phys->dd_origin_obj, FTAG, &findds); 00213 rw_exit(&dp->dp_config_rwlock); 00214 00215 if (relds) 00216 dsl_dataset_rele(relds, FTAG); 00217 00218 if (err) 00219 return (EXDEV); 00220 00221 relds = findds; 00222 } 00223 00224 if (relds) 00225 dsl_dataset_rele(relds, FTAG); 00226 00227 da.da_fp = fp; 00228 da.da_offp = offp; 00229 da.da_ddr.ddr_type = DDR_NONE; 00230 da.da_ddr.ddr_first = da.da_ddr.ddr_last = 0; 00231 da.da_err = 0; 00232 da.da_td = curthread; 00233 00234 err = traverse_dataset(ds, fromds->ds_phys->ds_creation_txg, 00235 TRAVERSE_PRE | TRAVERSE_PREFETCH_METADATA, diff_cb, &da); 00236 00237 if (err) { 00238 da.da_err = err; 00239 } else { 00240 /* we set the da.da_err we return as side-effect */ 00241 (void) write_record(&da); 00242 } 00243 00244 return (da.da_err); 00245 }