/* * smb_vop_link(target-dir-vp, source-file-vp, target-name) * * Create a link - same tree (identical TID) only. */ int smb_vop_link(vnode_t *to_dvp, vnode_t *from_vp, char *to_name, int flags, cred_t *cr) { int option_flags = 0; char *np, *buf; int rc; if (flags & SMB_IGNORE_CASE) option_flags = FIGNORECASE; if (flags & SMB_CATIA) { buf = kmem_zalloc(MAXNAMELEN, KM_SLEEP); np = smb_vop_catia_v5tov4(to_name, buf, MAXNAMELEN); if (strchr(np, '/') != NULL) { kmem_free(buf, MAXNAMELEN); return (EILSEQ); } rc = VOP_LINK(to_dvp, from_vp, np, cr, &smb_ct, option_flags); kmem_free(buf, MAXNAMELEN); return (rc); } rc = VOP_LINK(to_dvp, from_vp, to_name, cr, &smb_ct, option_flags); return (rc); }
STATIC int linvfs_link( struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) { struct inode *ip; /* inode of guy being linked to */ vnode_t *tdvp; /* target directory for new name/link */ vnode_t *vp; /* vp of name being linked */ int error; ip = old_dentry->d_inode; /* inode being linked to */ if (S_ISDIR(ip->i_mode)) return -EPERM; tdvp = LINVFS_GET_VP(dir); vp = LINVFS_GET_VP(ip); VOP_LINK(tdvp, vp, dentry, NULL, error); if (!error) { VMODIFY(tdvp); VN_HOLD(vp); validate_fields(ip); d_instantiate(dentry, ip); } return -error; }
static int zfs_replay_link(void *arg1, char *arg2, boolean_t byteswap) { zfsvfs_t *zfsvfs = (zfsvfs_t *)arg1; lr_link_t *lr = (lr_link_t *)arg2; char *name = (char *)(lr + 1); /* name follows lr_link_t */ znode_t *dzp, *zp; int error; int vflg = 0; if (byteswap) byteswap_uint64_array(lr, sizeof (*lr)); if ((error = zfs_zget(zfsvfs, lr->lr_doid, &dzp)) != 0) return (error); if ((error = zfs_zget(zfsvfs, lr->lr_link_obj, &zp)) != 0) { VN_RELE(ZTOV(dzp)); return (error); } if (lr->lr_common.lrc_txtype & TX_CI) vflg |= FIGNORECASE; error = VOP_LINK(ZTOV(dzp), ZTOV(zp), name, kcred, NULL, vflg); VN_RELE(ZTOV(zp)); VN_RELE(ZTOV(dzp)); return (error); }
/* Does most of the work for link(). */ int vfs_link(char *oldpath, char *newpath) { struct vnode *oldfile; struct vnode *newdir; char newname[NAME_MAX+1]; int result; result = vfs_lookup(oldpath, &oldfile); if (result) { return result; } result = vfs_lookparent(newpath, &newdir, newname, sizeof(newname)); if (result) { VOP_DECREF(oldfile); return result; } if (oldfile->vn_fs==NULL || newdir->vn_fs==NULL || oldfile->vn_fs != newdir->vn_fs) { VOP_DECREF(newdir); VOP_DECREF(oldfile); return EXDEV; } result = VOP_LINK(newdir, newname, oldfile); VOP_DECREF(newdir); VOP_DECREF(oldfile); return result; }
static int unionfs_link(void *v) { #if 0 struct vop_link_args *ap = v; int error; int needrelookup; struct unionfs_node *dunp; struct unionfs_node *unp; struct vnode *udvp; struct vnode *uvp; struct componentname *cnp; UNIONFS_INTERNAL_DEBUG("unionfs_link: enter\n"); error = 0; needrelookup = 0; dunp = VTOUNIONFS(ap->a_tdvp); unp = NULL; udvp = dunp->un_uppervp; uvp = NULLVP; cnp = ap->a_cnp; if (udvp == NULLVP) return (EROFS); if (ap->a_vp->v_op != unionfs_vnodeop_p) uvp = ap->a_vp; else { unp = VTOUNIONFS(ap->a_vp); if (unp->un_uppervp == NULLVP) { if (ap->a_vp->v_type != VREG) return (EOPNOTSUPP); error = unionfs_copyfile(unp, 1, cnp->cn_cred); if (error != 0) return (error); needrelookup = 1; } uvp = unp->un_uppervp; } if (needrelookup != 0) error = unionfs_relookup_for_create(ap->a_tdvp, cnp); if (error == 0) error = VOP_LINK(udvp, uvp, cnp); UNIONFS_INTERNAL_DEBUG("unionfs_link: leave (%d)\n", error); return (error); #else panic("XXXAD"); return 0; #endif }
int RUMP_VOP_LINK(struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { int error; rump_schedule(); error = VOP_LINK(dvp, vp, cnp); rump_unschedule(); return error; }
static int auto_link( vnode_t *tdvp, vnode_t *svp, char *nm, cred_t *cred, caller_context_t *ct, int flags) { vnode_t *newvp; int error; AUTOFS_DPRINT((4, "auto_link tdvp %p svp %p nm %s\n", (void *)tdvp, (void *)svp, nm)); if (error = auto_trigger_mount(tdvp, cred, &newvp)) goto done; if (newvp == NULL) { /* * an autonode can not be a link to another node */ error = ENOSYS; goto done; } if (vn_is_readonly(newvp)) { error = EROFS; VN_RELE(newvp); goto done; } if (vn_matchops(svp, auto_vnodeops)) { /* * source vp can't be an autonode */ error = ENOSYS; VN_RELE(newvp); goto done; } error = VOP_LINK(newvp, svp, nm, cred, ct, flags); VN_RELE(newvp); done: AUTOFS_DPRINT((5, "auto_link error=%d\n", error)); return (error); }
static int xattr_dir_link(vnode_t *tdvp, vnode_t *svp, char *name, cred_t *cr, caller_context_t *ct, int flags) { vnode_t *pvp; int error; if (svp->v_flag & V_SYSATTR) { return (EINVAL); } error = xattr_dir_realdir(tdvp, &pvp, LOOKUP_XATTR, cr, ct); if (error == 0) { error = VOP_LINK(pvp, svp, name, cr, ct, flags); } return (error); }
static int zfs_replay_link(zfsvfs_t *zfsvfs, void *data, boolean_t byteswap) { #ifdef __OSV__ kprintf("TX_LINK not supported on OSv\n"); return EOPNOTSUPP; #else lr_link_t *lr; char *name = (char *)(lr + 1); /* name follows lr_link_t */ znode_t *dzp, *zp; struct componentname cn; int error; int vflg = 0; if (byteswap) byteswap_uint64_array(lr, sizeof (*lr)); if ((error = zfs_zget(zfsvfs, lr->lr_doid, &dzp)) != 0) return (error); if ((error = zfs_zget(zfsvfs, lr->lr_link_obj, &zp)) != 0) { VN_RELE(ZTOV(dzp)); return (error); } if (lr->lr_common.lrc_txtype & TX_CI) vflg |= FIGNORECASE; cn.cn_nameptr = name; cn.cn_cred = kcred; cn.cn_thread = curthread; cn.cn_flags = SAVENAME; vn_lock(ZTOV(dzp), LK_EXCLUSIVE | LK_RETRY); vn_lock(ZTOV(zp), LK_EXCLUSIVE | LK_RETRY); error = VOP_LINK(ZTOV(dzp), ZTOV(zp), &cn /*,vflg*/); VOP_UNLOCK(ZTOV(zp), 0); VOP_UNLOCK(ZTOV(dzp), 0); VN_RELE(ZTOV(zp)); VN_RELE(ZTOV(dzp)); return (error); #endif }
static int zfs_replay_link(zfsvfs_t *zfsvfs, lr_link_t *lr, boolean_t byteswap) { char *name = (char *)(lr + 1); /* name follows lr_link_t */ znode_t *dzp, *zp; struct componentname cn; int error; int vflg = 0; if (byteswap) byteswap_uint64_array(lr, sizeof (*lr)); if ((error = zfs_zget(zfsvfs, lr->lr_doid, &dzp)) != 0) return (error); if ((error = zfs_zget(zfsvfs, lr->lr_link_obj, &zp)) != 0) { VN_RELE(ZTOV(dzp)); return (error); } if (lr->lr_common.lrc_txtype & TX_CI) vflg |= FIGNORECASE; cn.cn_nameptr = name; cn.cn_cred = kcred; cn.cn_flags = 0; vn_lock(ZTOV(dzp), LK_EXCLUSIVE | LK_RETRY); vn_lock(ZTOV(zp), LK_EXCLUSIVE | LK_RETRY); error = VOP_LINK(ZTOV(dzp), ZTOV(zp), &cn /*,vflg*/); VOP_UNLOCK(ZTOV(zp)); VOP_UNLOCK(ZTOV(dzp)); VN_RELE(ZTOV(zp)); VN_RELE(ZTOV(dzp)); return (error); }
static int zfsfuse_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, const char *newname) { if(strlen(newname) >= MAXNAMELEN) return ENAMETOOLONG; vfs_t *vfs = (vfs_t *) fuse_req_userdata(req); zfsvfs_t *zfsvfs = vfs->vfs_data; ZFS_ENTER(zfsvfs); znode_t *td_znode, *s_znode; int error = zfs_zget(zfsvfs, ino, &s_znode, B_FALSE); if(error) { ZFS_EXIT(zfsvfs); /* If the inode we are trying to get was recently deleted dnode_hold_impl will return EEXIST instead of ENOENT */ return error == EEXIST ? ENOENT : error; } ASSERT(s_znode != NULL); error = zfs_zget(zfsvfs, newparent, &td_znode, B_FALSE); if(error) { VN_RELE(ZTOV(s_znode)); ZFS_EXIT(zfsvfs); /* If the inode we are trying to get was recently deleted dnode_hold_impl will return EEXIST instead of ENOENT */ return error == EEXIST ? ENOENT : error; } vnode_t *svp = ZTOV(s_znode); vnode_t *tdvp = ZTOV(td_znode); ASSERT(svp != NULL); ASSERT(tdvp != NULL); cred_t cred; zfsfuse_getcred(req, &cred); error = VOP_LINK(tdvp, svp, (char *) newname, &cred, NULL, 0); vnode_t *vp = NULL; if(error) goto out; error = VOP_LOOKUP(tdvp, (char *) newname, &vp, NULL, 0, NULL, &cred, NULL, NULL, NULL); if(error) goto out; ASSERT(vp != NULL); struct fuse_entry_param e = { 0 }; e.attr_timeout = 0.0; e.entry_timeout = 0.0; e.ino = VTOZ(vp)->z_id; if(e.ino == 3) e.ino = 1; e.generation = VTOZ(vp)->z_phys->zp_gen; error = zfsfuse_stat(vp, &e.attr, &cred); out: if(vp != NULL) VN_RELE(vp); VN_RELE(tdvp); VN_RELE(svp); ZFS_EXIT(zfsvfs); if(!error) fuse_reply_entry(req, &e); return error; }
extern int vnode_iop_link( DENT_T * olddent, INODE_T * parent, DENT_T * newdent ) { int err = 0; struct link_ctx ctx; VATTR_T *vap; VNODE_T *parentvp; ASSERT_I_SEM_MINE(olddent->d_inode); ASSERT_I_SEM_MINE(parent); ASSERT(MDKI_INOISMVFS(parent)); if (!vnlayer_link_eligible(olddent)) return -EXDEV; /* VOP_REALVP will check that the parent is a loopback directory and * return EINVAL if it isn't. */ if (VOP_REALVP(ITOV(parent), &parentvp) == 0) { /* We are creating a shadow link so bypass the mvfs for the rest */ err = vnlayer_do_linux_link(parentvp, olddent, parent, newdent); err = mdki_errno_unix_to_linux(err); } else { /* This needs to be passed on to the mvfs to deal with */ CALL_DATA_T cd; INODE_T *inode; if (!MDKI_INOISOURS(olddent->d_inode)) return -EXDEV; ctx.parent = parent; ctx.newdent = newdent; ctx.olddent = olddent; ctx.done = FALSE; mdki_linux_init_call_data(&cd); if (MDKI_INOISMVFS(olddent->d_inode)) { err = VOP_LINK(ITOV(parent), ITOV(olddent->d_inode), (char *)newdent->d_name.name, &cd, &ctx); err = mdki_errno_unix_to_linux(err); if (err == 0 && !ctx.done) { /* Again, a heavy handed way of bumping the inode count and * handling the locking (This will use the inode lock) */ inode = igrab(olddent->d_inode); VNODE_D_INSTANTIATE(newdent, inode); if ((vap = VATTR_ALLOC()) != NULL) { VATTR_SET_MASK(vap, AT_ALL); if (VOP_GETATTR(ITOV(inode), vap, 0, &cd) == 0) mdki_linux_vattr_pullup(ITOV(inode), vap, AT_ALL); VATTR_FREE(vap); } } } else { err = -EXDEV; } mdki_linux_destroy_call_data(&cd); } return err; }
/* * union_link: * * tdvp will be locked on entry, vp will not be locked on entry. * tdvp should remain locked on return and vp should remain unlocked * on return. * * union_link(struct vnode *a_tdvp, struct vnode *a_vp, * struct componentname *a_cnp) */ static int union_link(struct vop_old_link_args *ap) { struct componentname *cnp = ap->a_cnp; struct thread *td = cnp->cn_td; struct union_node *dun = VTOUNION(ap->a_tdvp); struct vnode *vp; struct vnode *tdvp; int error = 0; if (ap->a_tdvp->v_ops != ap->a_vp->v_ops) { vp = ap->a_vp; } else { struct union_node *tun = VTOUNION(ap->a_vp); if (tun->un_uppervp == NULLVP) { vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY); #if 0 if (dun->un_uppervp == tun->un_dirvp) { if (dun->un_flags & UN_ULOCK) { dun->un_flags &= ~UN_ULOCK; vn_unlock(dun->un_uppervp); } } #endif error = union_copyup(tun, 1, cnp->cn_cred, td); #if 0 if (dun->un_uppervp == tun->un_dirvp) { vn_lock(dun->un_uppervp, LK_EXCLUSIVE | LK_RETRY); dun->un_flags |= UN_ULOCK; } #endif vn_unlock(ap->a_vp); } vp = tun->un_uppervp; } if (error) return (error); /* * Make sure upper is locked, then unlock the union directory we were * called with to avoid a deadlock while we are calling VOP_LINK on * the upper (with tdvp locked and vp not locked). Our ap->a_tdvp * is expected to be locked on return. */ if ((tdvp = union_lock_upper(dun, td)) == NULLVP) return (EROFS); vn_unlock(ap->a_tdvp); /* unlock calling node */ error = VOP_LINK(tdvp, vp, cnp); /* call link on upper */ /* * We have to unlock tdvp prior to relocking our calling node in * order to avoid a deadlock. */ union_unlock_upper(tdvp, td); vn_lock(ap->a_tdvp, LK_EXCLUSIVE | LK_RETRY); return (error); }