diff options
author | dchagin <dchagin@FreeBSD.org> | 2017-08-10 05:38:31 +0000 |
---|---|---|
committer | dchagin <dchagin@FreeBSD.org> | 2017-08-10 05:38:31 +0000 |
commit | 7fa36a1ea4124e7ecdcd10155a1df4bac2e0e774 (patch) | |
tree | ffa9487bf8060968f7dc55532290a773da5e2017 /sys/fs/fdescfs | |
parent | 58d831d1074873baa2c269c95d3912fd255de1cb (diff) | |
download | FreeBSD-src-7fa36a1ea4124e7ecdcd10155a1df4bac2e0e774.zip FreeBSD-src-7fa36a1ea4124e7ecdcd10155a1df4bac2e0e774.tar.gz |
MFC r321839:
Implement proper Linux /dev/fd and /proc/self/fd behavior by adding
Linux specific things to the native fdescfs file system.
Unlike FreeBSD, the Linux fdescfs is a directory containing a symbolic
links to the actual files, which the process has open.
A readlink(2) call on this file returns a full path in case of regular file
or a string in a special format (type:[inode], anon_inode:<file-type>, etc..).
As well as in a FreeBSD, opening the file in the Linux fdescfs directory is
equivalent to duplicating the corresponding file descriptor.
Here we have mutually exclusive requirements:
- in case of readlink(2) call fdescfs lookup() method should return VLNK
vnode otherwise our kern_readlink() fail with EINVAL error;
- in the other calls fdescfs lookup() method should return non VLNK vnode.
For what new vnode v_flag VV_READLINK was added, which is set if fdescfs has beed
mounted with linrdlnk option an modified kern_readlinkat() to properly handle it.
For now For Linux ABI compatibility mount fdescfs volume with linrdlnk option:
mount -t fdescfs -o linrdlnk null /compat/linux/dev/fd
Relnotes: yes
Differential Revision: https://reviews.freebsd.org/D11452
Diffstat (limited to 'sys/fs/fdescfs')
-rw-r--r-- | sys/fs/fdescfs/fdesc.h | 4 | ||||
-rw-r--r-- | sys/fs/fdescfs/fdesc_vfsops.c | 2 | ||||
-rw-r--r-- | sys/fs/fdescfs/fdesc_vnops.c | 60 |
3 files changed, 63 insertions, 3 deletions
diff --git a/sys/fs/fdescfs/fdesc.h b/sys/fs/fdescfs/fdesc.h index bbf9d77..3d4cfb4 100644 --- a/sys/fs/fdescfs/fdesc.h +++ b/sys/fs/fdescfs/fdesc.h @@ -38,7 +38,9 @@ #define _FS_FDESC_H_ /* Private mount flags for fdescfs. */ -#define FMNT_UNMOUNTF 0x01 +#define FMNT_UNMOUNTF 0x01 +#define FMNT_LINRDLNKF 0x02 + struct fdescmount { struct vnode *f_root; /* Root node */ int flags; diff --git a/sys/fs/fdescfs/fdesc_vfsops.c b/sys/fs/fdescfs/fdesc_vfsops.c index ef5fd5e..f63eea3 100644 --- a/sys/fs/fdescfs/fdesc_vfsops.c +++ b/sys/fs/fdescfs/fdesc_vfsops.c @@ -101,6 +101,8 @@ fdesc_mount(struct mount *mp) */ mp->mnt_data = fmp; fmp->flags = 0; + if (vfs_getopt(mp->mnt_optnew, "linrdlnk", NULL, NULL) == 0) + fmp->flags |= FMNT_LINRDLNKF; error = fdesc_allocvp(Froot, -1, FD_ROOT, mp, &rvp); if (error) { free(fmp, M_FDESCMNT); diff --git a/sys/fs/fdescfs/fdesc_vnops.c b/sys/fs/fdescfs/fdesc_vnops.c index 3bea640..c997212 100644 --- a/sys/fs/fdescfs/fdesc_vnops.c +++ b/sys/fs/fdescfs/fdesc_vnops.c @@ -69,6 +69,7 @@ static vop_getattr_t fdesc_getattr; static vop_lookup_t fdesc_lookup; static vop_open_t fdesc_open; static vop_readdir_t fdesc_readdir; +static vop_readlink_t fdesc_readlink; static vop_reclaim_t fdesc_reclaim; static vop_setattr_t fdesc_setattr; @@ -81,6 +82,7 @@ static struct vop_vector fdesc_vnodeops = { .vop_open = fdesc_open, .vop_pathconf = vop_stdpathconf, .vop_readdir = fdesc_readdir, + .vop_readlink = fdesc_readlink, .vop_reclaim = fdesc_reclaim, .vop_setattr = fdesc_setattr, }; @@ -195,6 +197,8 @@ loop: fd->fd_type = ftype; fd->fd_fd = fd_fd; fd->fd_ix = ix; + if (ftype == Fdesc && fmp->flags & FMNT_LINRDLNKF) + vp->v_vflag |= VV_READLINK; error = insmntque1(vp, mp, fdesc_insmntque_dtr, NULL); if (error != 0) { *vpp = NULLVP; @@ -420,7 +424,7 @@ fdesc_getattr(struct vop_getattr_args *ap) break; case Fdesc: - vap->va_type = VCHR; + vap->va_type = (vp->v_vflag & VV_READLINK) == 0 ? VCHR : VLNK; vap->va_nlink = 1; vap->va_size = 0; vap->va_rdev = makedev(0, vap->va_fileid); @@ -490,6 +494,7 @@ fdesc_setattr(struct vop_setattr_args *ap) static int fdesc_readdir(struct vop_readdir_args *ap) { + struct fdescmount *fmp; struct uio *uio = ap->a_uio; struct filedesc *fdp; struct dirent d; @@ -499,6 +504,7 @@ fdesc_readdir(struct vop_readdir_args *ap) if (VTOFDESC(ap->a_vp)->fd_type != Froot) panic("fdesc_readdir: not dir"); + fmp = VFSTOFDESC(ap->a_vp->v_mount); if (ap->a_ncookies != NULL) *ap->a_ncookies = 0; @@ -530,7 +536,8 @@ fdesc_readdir(struct vop_readdir_args *ap) break; dp->d_namlen = sprintf(dp->d_name, "%d", fcnt); dp->d_reclen = UIO_MX; - dp->d_type = DT_CHR; + dp->d_type = (fmp->flags & FMNT_LINRDLNKF) == 0 ? + DT_CHR : DT_LNK; dp->d_fileno = i + FD_DESC; break; } @@ -567,3 +574,52 @@ fdesc_reclaim(struct vop_reclaim_args *ap) vp->v_data = NULL; return (0); } + +static int +fdesc_readlink(struct vop_readlink_args *va) +{ + struct vnode *vp, *vn; + cap_rights_t rights; + struct thread *td; + struct uio *uio; + struct file *fp; + char *freepath, *fullpath; + size_t pathlen; + int lockflags, fd_fd; + int error; + + freepath = NULL; + vn = va->a_vp; + if (VTOFDESC(vn)->fd_type != Fdesc) + panic("fdesc_readlink: not fdescfs link"); + fd_fd = ((struct fdescnode *)vn->v_data)->fd_fd; + lockflags = VOP_ISLOCKED(vn); + VOP_UNLOCK(vn, 0); + + td = curthread; + error = fget_cap(td, fd_fd, cap_rights_init(&rights), &fp, NULL); + if (error != 0) + goto out; + + switch (fp->f_type) { + case DTYPE_VNODE: + vp = fp->f_vnode; + error = vn_fullpath(td, vp, &fullpath, &freepath); + break; + default: + fullpath = "anon_inode:[unknown]"; + break; + } + if (error == 0) { + uio = va->a_uio; + pathlen = strlen(fullpath); + error = uiomove(fullpath, pathlen, uio); + } + if (freepath != NULL) + free(freepath, M_TEMP); + fdrop(fp, td); + +out: + vn_lock(vn, lockflags | LK_RETRY); + return (error); +} |