/* ARGSUSED */ int zfsctl_snapdir_mkdir(struct inode *dip, char *dirname, vattr_t *vap, struct inode **ipp, cred_t *cr, int flags) { zfs_sb_t *zsb = ITOZSB(dip); char *dsname; int error; dsname = kmem_alloc(MAXNAMELEN, KM_SLEEP); if (snapshot_namecheck(dirname, NULL, NULL) != 0) { error = EILSEQ; goto out; } dmu_objset_name(zsb->z_os, dsname); error = zfs_secpolicy_snapshot_perms(dsname, cr); if (error) goto out; if (error == 0) { error = dmu_objset_snapshot(dsname, dirname, NULL, NULL, B_FALSE, B_FALSE, -1); if (error) goto out; error = zfsctl_snapdir_lookup(dip, dirname, ipp, 0, cr, NULL, NULL); } out: kmem_free(dsname, MAXNAMELEN); return (error); }
/* ARGSUSED */ static int zfsctl_snapdir_mkdir(vnode_t *dvp, char *dirname, vattr_t *vap, vnode_t **vpp, cred_t *cr, caller_context_t *cc, int flags, vsecattr_t *vsecp) { zfsvfs_t *zfsvfs = dvp->v_vfsp->vfs_data; char name[MAXNAMELEN]; int err; static enum symfollow follow = NO_FOLLOW; static enum uio_seg seg = UIO_SYSSPACE; if (snapshot_namecheck(dirname, NULL, NULL) != 0) return (EILSEQ); dmu_objset_name(zfsvfs->z_os, name); *vpp = NULL; err = zfs_secpolicy_snapshot_perms(name, cr); if (err) return (err); if (err == 0) { err = dmu_objset_snapshot(name, dirname, NULL, B_FALSE); if (err) return (err); err = lookupnameat(dirname, seg, follow, NULL, vpp, dvp); } return (err); }
void zfs_fs_name_fn(void *ptr, char *osname) { zfsvfs_t *zfsvfs = NULL; ASSERT(ptr); zfsvfs = (zfsvfs_t *) ptr; dmu_objset_name(zfsvfs->z_os, osname); }
static int zfsctl_snapshot_zname(vnode_t *vp, const char *name, int len, char *zname) { objset_t *os = ((zfsvfs_t *)((vp)->v_vfsp->vfs_data))->z_os; if (snapshot_namecheck(name, NULL, NULL) != 0) return (EILSEQ); dmu_objset_name(os, zname); if (strlen(zname) + 1 + strlen(name) >= len) return (ENAMETOOLONG); (void) strcat(zname, "@"); (void) strcat(zname, name); return (0); }
/* * Block out VOPs and close zfsvfs_t::z_os * * Note, if successful, then we return with the 'z_teardown_lock' and * 'z_teardown_inactive_lock' write held. */ int zfs_suspend_fs(zfsvfs_t *zfsvfs, char *name, int *mode) { int error; if ((error = zfsvfs_teardown(zfsvfs, B_FALSE)) != 0) return (error); *mode = zfsvfs->z_os->os_mode; dmu_objset_name(zfsvfs->z_os, name); dmu_objset_close(zfsvfs->z_os); return (0); }
static int zfsctl_snapshot_zname(struct inode *ip, const char *name, int len, char *zname) { objset_t *os = ITOZSB(ip)->z_os; if (snapshot_namecheck(name, NULL, NULL) != 0) return (EILSEQ); dmu_objset_name(os, zname); if ((strlen(zname) + 1 + strlen(name)) >= len) return (ENAMETOOLONG); (void) strcat(zname, "@"); (void) strcat(zname, name); return (0); }
/*ARGSUSED*/ static int zfsctl_snapdir_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr, caller_context_t *ct, int flags) { zfsctl_snapdir_t *sdp = sdvp->v_data; zfs_snapentry_t search, *sep; zfsvfs_t *zfsvfs; avl_index_t where; char from[ZFS_MAX_DATASET_NAME_LEN], to[ZFS_MAX_DATASET_NAME_LEN]; char real[ZFS_MAX_DATASET_NAME_LEN], fsname[ZFS_MAX_DATASET_NAME_LEN]; int err; zfsvfs = sdvp->v_vfsp->vfs_data; ZFS_ENTER(zfsvfs); if ((flags & FIGNORECASE) || zfsvfs->z_case == ZFS_CASE_INSENSITIVE) { err = dmu_snapshot_realname(zfsvfs->z_os, snm, real, sizeof (real), NULL); if (err == 0) { snm = real; } else if (err != ENOTSUP) { ZFS_EXIT(zfsvfs); return (err); } } ZFS_EXIT(zfsvfs); dmu_objset_name(zfsvfs->z_os, fsname); err = zfsctl_snapshot_zname(sdvp, snm, sizeof (from), from); if (err == 0) err = zfsctl_snapshot_zname(tdvp, tnm, sizeof (to), to); if (err == 0) err = zfs_secpolicy_rename_perms(from, to, cr); if (err != 0) return (err); /* * Cannot move snapshots out of the snapdir. */ if (sdvp != tdvp) return (SET_ERROR(EINVAL)); if (strcmp(snm, tnm) == 0) return (0); mutex_enter(&sdp->sd_lock); search.se_name = (char *)snm; if ((sep = avl_find(&sdp->sd_snaps, &search, &where)) == NULL) { mutex_exit(&sdp->sd_lock); return (SET_ERROR(ENOENT)); } err = dsl_dataset_rename_snapshot(fsname, snm, tnm, B_FALSE); if (err == 0) zfsctl_rename_snap(sdp, sep, tnm); mutex_exit(&sdp->sd_lock); return (err); }
static int zfs_register_callbacks(vfs_t *vfsp) { struct dsl_dataset *ds = NULL; objset_t *os = NULL; zfsvfs_t *zfsvfs = NULL; uint64_t nbmand; int readonly, do_readonly = FALSE; int setuid, do_setuid = FALSE; int exec, do_exec = FALSE; int xattr, do_xattr = FALSE; int atime, do_atime = FALSE; int error = 0; ASSERT(vfsp); zfsvfs = vfsp->vfs_data; ASSERT(zfsvfs); os = zfsvfs->z_os; /* * This function can be called for a snapshot when we update snapshot's * mount point, which isn't really supported. */ if (dmu_objset_is_snapshot(os)) return (EOPNOTSUPP); /* * The act of registering our callbacks will destroy any mount * options we may have. In order to enable temporary overrides * of mount options, we stash away the current values and * restore them after we register the callbacks. */ if (vfs_optionisset(vfsp, MNTOPT_RO, NULL)) { readonly = B_TRUE; do_readonly = B_TRUE; } else if (vfs_optionisset(vfsp, MNTOPT_RW, NULL)) { readonly = B_FALSE; do_readonly = B_TRUE; } if (vfs_optionisset(vfsp, MNTOPT_NOSUID, NULL)) { setuid = B_FALSE; do_setuid = B_TRUE; } else { if (vfs_optionisset(vfsp, MNTOPT_NOSETUID, NULL)) { setuid = B_FALSE; do_setuid = B_TRUE; } else if (vfs_optionisset(vfsp, MNTOPT_SETUID, NULL)) { setuid = B_TRUE; do_setuid = B_TRUE; } } if (vfs_optionisset(vfsp, MNTOPT_NOEXEC, NULL)) { exec = B_FALSE; do_exec = B_TRUE; } else if (vfs_optionisset(vfsp, MNTOPT_EXEC, NULL)) { exec = B_TRUE; do_exec = B_TRUE; } if (vfs_optionisset(vfsp, MNTOPT_NOXATTR, NULL)) { xattr = B_FALSE; do_xattr = B_TRUE; } else if (vfs_optionisset(vfsp, MNTOPT_XATTR, NULL)) { xattr = B_TRUE; do_xattr = B_TRUE; } if (vfs_optionisset(vfsp, MNTOPT_NOATIME, NULL)) { atime = B_FALSE; do_atime = B_TRUE; } else if (vfs_optionisset(vfsp, MNTOPT_ATIME, NULL)) { atime = B_TRUE; do_atime = B_TRUE; } /* * nbmand is a special property. It can only be changed at * mount time. * * This is weird, but it is documented to only be changeable * at mount time. */ if (vfs_optionisset(vfsp, MNTOPT_NONBMAND, NULL)) { nbmand = B_FALSE; } else if (vfs_optionisset(vfsp, MNTOPT_NBMAND, NULL)) { nbmand = B_TRUE; } else { char osname[MAXNAMELEN]; dmu_objset_name(os, osname); if (error = dsl_prop_get_integer(osname, "nbmand", &nbmand, NULL)) { return (error); } } /* * Register property callbacks. * * It would probably be fine to just check for i/o error from * the first prop_register(), but I guess I like to go * overboard... */ ds = dmu_objset_ds(os); error = dsl_prop_register(ds, "atime", atime_changed_cb, zfsvfs); error = error ? error : dsl_prop_register(ds, "xattr", xattr_changed_cb, zfsvfs); error = error ? error : dsl_prop_register(ds, "recordsize", blksz_changed_cb, zfsvfs); error = error ? error : dsl_prop_register(ds, "readonly", readonly_changed_cb, zfsvfs); error = error ? error : dsl_prop_register(ds, "setuid", setuid_changed_cb, zfsvfs); error = error ? error : dsl_prop_register(ds, "exec", exec_changed_cb, zfsvfs); error = error ? error : dsl_prop_register(ds, "snapdir", snapdir_changed_cb, zfsvfs); error = error ? error : dsl_prop_register(ds, "aclmode", acl_mode_changed_cb, zfsvfs); error = error ? error : dsl_prop_register(ds, "aclinherit", acl_inherit_changed_cb, zfsvfs); error = error ? error : dsl_prop_register(ds, "vscan", vscan_changed_cb, zfsvfs); if (error) goto unregister; /* * Invoke our callbacks to restore temporary mount options. */ if (do_readonly) readonly_changed_cb(zfsvfs, readonly); if (do_setuid) setuid_changed_cb(zfsvfs, setuid); if (do_exec) exec_changed_cb(zfsvfs, exec); if (do_xattr) xattr_changed_cb(zfsvfs, xattr); if (do_atime) atime_changed_cb(zfsvfs, atime); nbmand_changed_cb(zfsvfs, nbmand); return (0); unregister: /* * We may attempt to unregister some callbacks that are not * registered, but this is OK; it will simply return ENOMSG, * which we will ignore. */ (void) dsl_prop_unregister(ds, "atime", atime_changed_cb, zfsvfs); (void) dsl_prop_unregister(ds, "xattr", xattr_changed_cb, zfsvfs); (void) dsl_prop_unregister(ds, "recordsize", blksz_changed_cb, zfsvfs); (void) dsl_prop_unregister(ds, "readonly", readonly_changed_cb, zfsvfs); (void) dsl_prop_unregister(ds, "setuid", setuid_changed_cb, zfsvfs); (void) dsl_prop_unregister(ds, "exec", exec_changed_cb, zfsvfs); (void) dsl_prop_unregister(ds, "snapdir", snapdir_changed_cb, zfsvfs); (void) dsl_prop_unregister(ds, "aclmode", acl_mode_changed_cb, zfsvfs); (void) dsl_prop_unregister(ds, "aclinherit", acl_inherit_changed_cb, zfsvfs); (void) dsl_prop_unregister(ds, "vscan", vscan_changed_cb, zfsvfs); return (error); }
static void zil_replay_log_record(zilog_t *zilog, lr_t *lr, void *zra, uint64_t claim_txg) { zil_replay_arg_t *zr = zra; const zil_header_t *zh = zilog->zl_header; uint64_t reclen = lr->lrc_reclen; uint64_t txtype = lr->lrc_txtype; char *name; int pass, error; if (!zilog->zl_replay) /* giving up */ return; if (lr->lrc_txg < claim_txg) /* already committed */ return; if (lr->lrc_seq <= zh->zh_replay_seq) /* already replayed */ return; /* Strip case-insensitive bit, still present in log record */ txtype &= ~TX_CI; if (txtype == 0 || txtype >= TX_MAX_TYPE) { error = EINVAL; goto bad; } /* * Make a copy of the data so we can revise and extend it. */ bcopy(lr, zr->zr_lrbuf, reclen); /* * The log block containing this lr may have been byteswapped * so that we can easily examine common fields like lrc_txtype. * However, the log is a mix of different data types, and only the * replay vectors know how to byteswap their records. Therefore, if * the lr was byteswapped, undo it before invoking the replay vector. */ if (zr->zr_byteswap) byteswap_uint64_array(zr->zr_lrbuf, reclen); /* * If this is a TX_WRITE with a blkptr, suck in the data. */ if (txtype == TX_WRITE && reclen == sizeof (lr_write_t)) { lr_write_t *lrw = (lr_write_t *)lr; blkptr_t *wbp = &lrw->lr_blkptr; uint64_t wlen = lrw->lr_length; char *wbuf = zr->zr_lrbuf + reclen; if (BP_IS_HOLE(wbp)) { /* compressed to a hole */ bzero(wbuf, wlen); } else { /* * A subsequent write may have overwritten this block, * in which case wbp may have been been freed and * reallocated, and our read of wbp may fail with a * checksum error. We can safely ignore this because * the later write will provide the correct data. */ zbookmark_t zb; zb.zb_objset = dmu_objset_id(zilog->zl_os); zb.zb_object = lrw->lr_foid; zb.zb_level = -1; zb.zb_blkid = lrw->lr_offset / BP_GET_LSIZE(wbp); (void) zio_wait(zio_read(NULL, zilog->zl_spa, wbp, wbuf, BP_GET_LSIZE(wbp), NULL, NULL, ZIO_PRIORITY_SYNC_READ, ZIO_FLAG_CANFAIL | ZIO_FLAG_SPECULATIVE, &zb)); (void) memmove(wbuf, wbuf + lrw->lr_blkoff, wlen); } } /* * We must now do two things atomically: replay this log record, * and update the log header sequence number to reflect the fact that * we did so. At the end of each replay function the sequence number * is updated if we are in replay mode. */ for (pass = 1; pass <= 2; pass++) { zilog->zl_replaying_seq = lr->lrc_seq; /* Only byteswap (if needed) on the 1st pass. */ error = zr->zr_replay[txtype](zr->zr_arg, zr->zr_lrbuf, zr->zr_byteswap && pass == 1); if (!error) return; /* * The DMU's dnode layer doesn't see removes until the txg * commits, so a subsequent claim can spuriously fail with * EEXIST. So if we receive any error we try syncing out * any removes then retry the transaction. */ if (pass == 1) txg_wait_synced(spa_get_dsl(zilog->zl_spa), 0); } bad: ASSERT(error); name = kmem_alloc(MAXNAMELEN, KM_SLEEP); dmu_objset_name(zr->zr_os, name); cmn_err(CE_WARN, "ZFS replay transaction error %d, " "dataset %s, seq 0x%llx, txtype %llu %s\n", error, name, (u_longlong_t)lr->lrc_seq, (u_longlong_t)txtype, (lr->lrc_txtype & TX_CI) ? "CI" : ""); zilog->zl_replay = B_FALSE; kmem_free(name, MAXNAMELEN); }
static void zil_replay_log_record(zilog_t *zilog, lr_t *lr, void *zra, uint64_t claim_txg) { zil_replay_arg_t *zr = zra; const zil_header_t *zh = zilog->zl_header; uint64_t reclen = lr->lrc_reclen; uint64_t txtype = lr->lrc_txtype; char *name; int pass, error; if (!zilog->zl_replay) /* giving up */ return; if (lr->lrc_txg < claim_txg) /* already committed */ return; if (lr->lrc_seq <= zh->zh_replay_seq) /* already replayed */ return; /* Strip case-insensitive bit, still present in log record */ txtype &= ~TX_CI; if (txtype == 0 || txtype >= TX_MAX_TYPE) { error = EINVAL; goto bad; } /* * Make a copy of the data so we can revise and extend it. */ bcopy(lr, zr->zr_lrbuf, reclen); /* * The log block containing this lr may have been byteswapped * so that we can easily examine common fields like lrc_txtype. * However, the log is a mix of different data types, and only the * replay vectors know how to byteswap their records. Therefore, if * the lr was byteswapped, undo it before invoking the replay vector. */ if (zr->zr_byteswap) byteswap_uint64_array(zr->zr_lrbuf, reclen); /* * We must now do two things atomically: replay this log record, * and update the log header sequence number to reflect the fact that * we did so. At the end of each replay function the sequence number * is updated if we are in replay mode. */ for (pass = 1; pass <= 2; pass++) { zilog->zl_replaying_seq = lr->lrc_seq; /* Only byteswap (if needed) on the 1st pass. */ error = zr->zr_replay[txtype](zr->zr_arg, zr->zr_lrbuf, zr->zr_byteswap && pass == 1); if (!error) return; /* * The DMU's dnode layer doesn't see removes until the txg * commits, so a subsequent claim can spuriously fail with * EEXIST. So if we receive any error we try syncing out * any removes then retry the transaction. */ if (pass == 1) txg_wait_synced(spa_get_dsl(zilog->zl_spa), 0); } bad: ASSERT(error); name = kmem_alloc(MAXNAMELEN, KM_SLEEP); dmu_objset_name(zr->zr_os, name); cmn_err(CE_WARN, "ZFS replay transaction error %d, " "dataset %s, seq 0x%llx, txtype %llu %s\n", error, name, (u_longlong_t)lr->lrc_seq, (u_longlong_t)txtype, (lr->lrc_txtype & TX_CI) ? "CI" : ""); zilog->zl_replay = B_FALSE; kmem_free(name, MAXNAMELEN); }