diff --git a/include/unistd.h b/include/unistd.h --- a/include/unistd.h +++ b/include/unistd.h @@ -519,6 +519,8 @@ int iruserok_sa(const void *, int, int, const char *, const char *); int issetugid(void); void __FreeBSD_libc_enter_restricted_mode(void); +int kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, + unsigned long idx2);(); long lpathconf(const char *, int); #ifndef _MKDTEMP_DECLARED char *mkdtemp(char *); diff --git a/lib/libc/sys/Symbol.map b/lib/libc/sys/Symbol.map --- a/lib/libc/sys/Symbol.map +++ b/lib/libc/sys/Symbol.map @@ -424,6 +424,10 @@ timerfd_settime; }; +FBSD_1.8 { + kcmp; +}; + FBSDprivate_1.0 { /* System call stubs */ ___acl_aclcheck_fd; diff --git a/sys/compat/linuxkpi/common/src/linux_compat.c b/sys/compat/linuxkpi/common/src/linux_compat.c --- a/sys/compat/linuxkpi/common/src/linux_compat.c +++ b/sys/compat/linuxkpi/common/src/linux_compat.c @@ -1721,6 +1721,19 @@ return (minor(ldev->dev)); } +static int +linux_file_kcmp(struct file *fp1, struct file *fp2, struct thread *td) +{ + struct linux_file *filp1, *filp2; + + if (fp2->f_type != DTYPE_DEV) + return (3); + + filp1 = fp1->f_data; + filp2 = fp2->f_data; + return (kcmp_cmp((uintptr_t)filp1->f_cdev, (uintptr_t)filp2->f_cdev)); +} + struct fileops linuxfileops = { .fo_read = linux_file_read, .fo_write = linux_file_write, @@ -1735,6 +1748,7 @@ .fo_chmod = invfo_chmod, .fo_chown = invfo_chown, .fo_sendfile = invfo_sendfile, + .fo_cmp = linux_file_kcmp, .fo_flags = DFLAG_PASSABLE, }; diff --git a/sys/fs/devfs/devfs_vnops.c b/sys/fs/devfs/devfs_vnops.c --- a/sys/fs/devfs/devfs_vnops.c +++ b/sys/fs/devfs/devfs_vnops.c @@ -2010,6 +2010,14 @@ return (cdev2priv(x)->cdp_inode); } +static int +devfs_cmp_f(struct file *fp1, struct file *fp2, struct thread *td) +{ + if (fp2->f_type != DTYPE_VNODE || fp2->f_ops != &devfs_ops_f) + return (3); + return (kcmp_cmp((uintptr_t)fp1->f_data, (uintptr_t)fp2->f_data)); +} + static struct fileops devfs_ops_f = { .fo_read = devfs_read_f, .fo_write = devfs_write_f, @@ -2025,6 +2033,7 @@ .fo_seek = vn_seek, .fo_fill_kinfo = vn_fill_kinfo, .fo_mmap = devfs_mmap_f, + .fo_cmp = devfs_cmp_f, .fo_flags = DFLAG_PASSABLE | DFLAG_SEEKABLE }; diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -2279,7 +2279,7 @@ * 3. after the lock is observed as not taken, any fdhold/pdhold calls are * guaranteed to see NULL, making it safe to finish clearing */ -static struct filedesc * +struct filedesc * fdhold(struct proc *p) { struct filedesc *fdp; @@ -2303,7 +2303,7 @@ return (pdp); } -static void +void fddrop(struct filedesc *fdp) { @@ -5253,6 +5253,7 @@ .fo_chown = badfo_chown, .fo_sendfile = badfo_sendfile, .fo_fill_kinfo = vn_fill_kinfo, + .fo_cmp = vn_cmp, .fo_flags = DFLAG_PASSABLE, }; diff --git a/sys/kern/sys_generic.c b/sys/kern/sys_generic.c --- a/sys/kern/sys_generic.c +++ b/sys/kern/sys_generic.c @@ -65,6 +65,7 @@ #include #include #include +#include #include #include #include @@ -2071,3 +2072,131 @@ td->td_retval[0] = error; return (0); } + +int +kcmp_cmp(uintptr_t a, uintptr_t b) +{ + if (a == b) + return (0); + else if (a < b) + return (1); + return (2); +} + +static int +kcmp_pget(struct thread *td, pid_t pid, struct proc **pp) +{ + int error; + if (pid != td->td_proc->p_pid) { + error = pget(pid, PGET_CANDEBUG | PGET_NOTWEXIT | PGET_HOLD, + pp); + } else { + error = 0; + *pp = td->td_proc; + } + return (error); +} + +static int +kcmp_getfp(struct thread *td, struct proc *p, int fd, struct file **fpp) +{ + struct filedesc *fdp; + struct file *fp; + int error; + + PROC_LOCK(p); + fdp = fdhold(p); + PROC_UNLOCK(p); + fp = NULL; + if (fdp == NULL) + return (ENOENT); + error = 0; + FILEDESC_SLOCK(fdp); + if (refcount_load(&fdp->fd_refcnt) != 0) { + fp = fget_noref(fdp, fd); + if (fp != NULL && fhold(fp)) { + *fpp = fp; + } else { + error = EBADF; + } + } else { + error = ENOENT; + } + FILEDESC_SUNLOCK(fdp); + fddrop(fdp); + return (error); +} + +int +kern_kcmp(struct thread *td, pid_t pid1, pid_t pid2, int type, + unsigned long idx1, unsigned long idx2) +{ + struct proc *p1, *p2; + struct file *fp1, *fp2; + int error, res; + + res = -1; + p1 = p2 = NULL; + error = kcmp_pget(td, pid1, &p1); + if (error == 0) + error = kcmp_pget(td, pid2, &p2); + if (error != 0) + goto out; + + switch (type) { + case KCMP_FILE: + case KCMP_FILEOBJ: + error = kcmp_getfp(td, p1, idx1, &fp1); + if (error == 0) { + error = kcmp_getfp(td, p2, idx2, &fp2); + if (error == 0) { + if (type == KCMP_FILEOBJ) + res = fo_cmp(fp1, fp2, td); + else + res = kcmp_cmp((uintptr_t)fp1, + (uintptr_t)fp2); + fdrop(fp2, td); + } + fdrop(fp1, td); + } + break; + case KCMP_FILES: + res = kcmp_cmp((uintptr_t)p1->p_fd, (uintptr_t)p2->p_fd); + break; + case KCMP_SIGHAND: + res = kcmp_cmp((uintptr_t)p1->p_sigacts, + (uintptr_t)p2->p_sigacts); + break; + case KCMP_VM: + res = kcmp_cmp((uintptr_t)p1->p_vmspace, + (uintptr_t)p2->p_vmspace); + break; + default: + error = EINVAL; + break; + } + +out: + if (p1 != NULL && p1 != td->td_proc) + PRELE(p1); + if (p2 != NULL && p2 != td->td_proc) + PRELE(p2); + + td->td_retval[0] = res; + return (error); +} + +int +sys_kcmp(struct thread *td, struct kcmp_args *uap) +{ + return (kern_kcmp(td, uap->pid1, uap->pid2, uap->type, + uap->idx1, uap->idx2)); +} + +int +file_kcmp_generic(struct file *fp1, struct file *fp2, struct thread *td) +{ + if (fp1->f_type != fp2->f_type) + return (3); + return (kcmp_cmp((uintptr_t)fp1->f_data, (uintptr_t)fp2->f_data)); +} diff --git a/sys/kern/sys_pipe.c b/sys/kern/sys_pipe.c --- a/sys/kern/sys_pipe.c +++ b/sys/kern/sys_pipe.c @@ -165,6 +165,7 @@ .fo_chown = pipe_chown, .fo_sendfile = invfo_sendfile, .fo_fill_kinfo = pipe_fill_kinfo, + .fo_cmp = file_kcmp_generic, .fo_flags = DFLAG_PASSABLE }; diff --git a/sys/kern/sys_socket.c b/sys/kern/sys_socket.c --- a/sys/kern/sys_socket.c +++ b/sys/kern/sys_socket.c @@ -109,6 +109,7 @@ .fo_sendfile = invfo_sendfile, .fo_fill_kinfo = soo_fill_kinfo, .fo_aio_queue = soo_aio_queue, + .fo_cmp = file_kcmp_generic, .fo_flags = DFLAG_PASSABLE }; diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -3329,6 +3329,14 @@ _Out_opt_ _Contains_long_timet_ struct itimerspec *old_value ); } - +588 AUE_NULL STD { + int kcmp( + pid_t pid1, + pid_t pid2, + int type, + unsigned long idx1, + unsigned long idx2 + ); + } ; vim: syntax=off diff --git a/sys/kern/uipc_shm.c b/sys/kern/uipc_shm.c --- a/sys/kern/uipc_shm.c +++ b/sys/kern/uipc_shm.c @@ -169,6 +169,7 @@ .fo_add_seals = shm_add_seals, .fo_fallocate = shm_fallocate, .fo_fspacectl = shm_fspacectl, + .fo_cmp = file_kcmp_generic, .fo_flags = DFLAG_PASSABLE | DFLAG_SEEKABLE, }; diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c --- a/sys/kern/vfs_vnops.c +++ b/sys/kern/vfs_vnops.c @@ -123,6 +123,7 @@ .fo_mmap = vn_mmap, .fo_fallocate = vn_fallocate, .fo_fspacectl = vn_fspacectl, + .fo_cmp = vn_cmp, .fo_flags = DFLAG_PASSABLE | DFLAG_SEEKABLE }; @@ -4228,3 +4229,11 @@ return (LK_SHARED); return (LK_EXCLUSIVE); } + +int +vn_cmp(struct file *fp1, struct file *fp2, struct thread *td) +{ + if (fp2->f_type != DTYPE_VNODE) + return (3); + return (kcmp_cmp((uintptr_t)fp1->f_vnode, (uintptr_t)fp2->f_vnode)); +} diff --git a/sys/sys/file.h b/sys/sys/file.h --- a/sys/sys/file.h +++ b/sys/sys/file.h @@ -129,6 +129,7 @@ typedef int fo_fspacectl_t(struct file *fp, int cmd, off_t *offset, off_t *length, int flags, struct ucred *active_cred, struct thread *td); +typedef int fo_cmp_t(struct file *fp, struct file *fp1, struct thread *td); typedef int fo_spare_t(struct file *fp); typedef int fo_flags_t; @@ -152,6 +153,7 @@ fo_get_seals_t *fo_get_seals; fo_fallocate_t *fo_fallocate; fo_fspacectl_t *fo_fspacectl; + fo_cmp_t *fo_cmp; fo_spare_t *fo_spares[8]; /* Spare slots */ fo_flags_t fo_flags; /* DFLAG_* below */ }; @@ -276,6 +278,7 @@ fo_fill_kinfo_t vn_fill_kinfo; fo_kqfilter_t vn_kqfilter_opath; int vn_fill_kinfo_vnode(struct vnode *vp, struct kinfo_file *kif); +int file_kcmp_generic(struct file *fp1, struct file *fp2, struct thread *td); void finit(struct file *, u_int, short, void *, struct fileops *); void finit_vnode(struct file *, u_int, void *, struct fileops *); @@ -489,6 +492,14 @@ active_cred, td)); } +static __inline int +fo_cmp(struct file *fp1, struct file *fp2, struct thread *td) +{ + + if (fp1->f_ops->fo_cmp == NULL) + return (EBADF); + return ((*fp1->f_ops->fo_cmp)(fp1, fp2, td)); +} #endif /* _KERNEL */ diff --git a/sys/sys/filedesc.h b/sys/sys/filedesc.h --- a/sys/sys/filedesc.h +++ b/sys/sys/filedesc.h @@ -262,6 +262,8 @@ void fdclose(struct thread *td, struct file *fp, int idx); void fdcloseexec(struct thread *td); void fdsetugidsafety(struct thread *td); +struct filedesc *fdhold(struct proc *p); +void fddrop(struct filedesc *fdp); struct filedesc *fdcopy(struct filedesc *fdp); void fdunshare(struct thread *td); void fdescfree(struct thread *td); diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h --- a/sys/sys/syscallsubr.h +++ b/sys/sys/syscallsubr.h @@ -198,6 +198,8 @@ int kern_jail(struct thread *td, struct jail *j); int kern_jail_get(struct thread *td, struct uio *options, int flags); int kern_jail_set(struct thread *td, struct uio *options, int flags); +int kern_kcmp(struct thread *td, pid_t pid1, pid_t pid2, int type, + unsigned long idx1, unsigned long idx2); int kern_kevent(struct thread *td, int fd, int nchanges, int nevents, struct kevent_copyops *k_ops, const struct timespec *timeout); int kern_kevent_anonymous(struct thread *td, int nevents, diff --git a/sys/sys/systm.h b/sys/sys/systm.h --- a/sys/sys/systm.h +++ b/sys/sys/systm.h @@ -485,6 +485,8 @@ /* XXX: Should be void nanodelay(u_int nsec); */ void DELAY(int usec); +int kcmp_cmp(uintptr_t a, uintptr_t b); + /* Root mount holdback API */ struct root_hold_token { int flags; diff --git a/sys/sys/unistd.h b/sys/sys/unistd.h --- a/sys/sys/unistd.h +++ b/sys/sys/unistd.h @@ -195,6 +195,13 @@ RFPROCDESC | RFSPAWN | RFPPWAIT) #define RFKERNELONLY (RFSTOPPED | RFHIGHPID | RFPROCDESC) +/* kcmp() options. */ +#define KCMP_FILE 100 +#define KCMP_FILEOBJ 101 +#define KCMP_FILES 102 +#define KCMP_SIGHAND 103 +#define KCMP_VM 104 + #define SWAPOFF_FORCE 0x00000001 /* diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h --- a/sys/sys/vnode.h +++ b/sys/sys/vnode.h @@ -818,6 +818,7 @@ void *alloc_arg, int lkflags, struct vnode **rvp); int vn_utimes_perm(struct vnode *vp, struct vattr *vap, struct ucred *cred, struct thread *td); +int vn_cmp(struct file *, struct file *, struct thread *td); int vn_io_fault_uiomove(char *data, int xfersize, struct uio *uio); int vn_io_fault_pgmove(vm_page_t ma[], vm_offset_t offset, int xfersize,