#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/uio.h>
#include <sys/malloc.h>
#include <sys/sysmsg.h>
#include <sys/conf.h>
#include <sys/filedesc.h>
#include <sys/sysctl.h>
#include <sys/vnode.h>
#include <sys/proc.h>
#include <sys/caps.h>
#include <sys/nlookup.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/filio.h>
#include <sys/fcntl.h>
#include <sys/unistd.h>
#include <sys/resourcevar.h>
#include <sys/event.h>
#include <sys/mman.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <sys/lock.h>
#include <vm/pmap.h>
#include <vm/vm_map.h>
#include <vm/vm_object.h>
#include <vm/vm_page.h>
#include <vm/vm_pager.h>
#include <vm/vm_pageout.h>
#include <vm/vm_extern.h>
#include <vm/vm_kern.h>
#include <sys/file2.h>
#include <machine/limits.h>
typedef struct file *file_t;
int
fp_open(const char *path, int flags, int mode, file_t *fpp)
{
struct nlookupdata nd;
struct thread *td;
int error;
if ((error = falloc(NULL, fpp, NULL)) != 0)
return (error);
td = curthread;
if (td->td_proc)
fsetcred(*fpp, td->td_proc->p_ucred);
error = nlookup_init(&nd, path, UIO_SYSSPACE, NLC_LOCKVP);
flags = FFLAGS(flags);
if (error == 0)
error = vn_open(&nd, fpp, flags, mode);
nlookup_done(&nd);
if (error) {
fdrop(*fpp);
*fpp = NULL;
}
return(error);
}
int
fp_vpopen(struct vnode *vp, int flags, file_t *fpp)
{
struct thread *td;
struct file *fp;
int vmode;
int error;
td = curthread;
if (vp->v_type == VLNK) {
error = EMLINK;
goto bad2;
}
if (vp->v_type == VSOCK) {
error = EOPNOTSUPP;
goto bad2;
}
flags = FFLAGS(flags);
vmode = 0;
if (flags & (FWRITE | O_TRUNC)) {
if (vp->v_type == VDIR) {
error = EISDIR;
goto bad2;
}
error = vn_writechk(vp);
if (error)
goto bad2;
vmode |= VWRITE;
}
if (flags & FREAD)
vmode |= VREAD;
if (vmode) {
error = VOP_ACCESS(vp, vmode, td->td_proc->p_ucred);
if (error)
goto bad2;
}
if ((error = falloc(NULL, fpp, NULL)) != 0)
goto bad2;
if (td->td_proc)
fsetcred(*fpp, td->td_proc->p_ucred);
error = VOP_OPEN(vp, flags, td->td_proc->p_ucred, fpp);
if (error)
goto bad1;
vput(vp);
return (0);
bad1:
fp = *fpp;
fp->f_ops = &badfileops;
fp->f_data = NULL;
fdrop(fp);
bad2:
*fpp = NULL;
return (error);
}
int
fp_pread(file_t fp, void *buf, size_t nbytes, off_t offset, ssize_t *res,
enum uio_seg seg)
{
struct uio auio;
struct iovec aiov;
size_t count;
int error;
if (res)
*res = 0;
if (nbytes > LONG_MAX)
return (EINVAL);
bzero(&auio, sizeof(auio));
aiov.iov_base = (caddr_t)buf;
aiov.iov_len = nbytes;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_offset = offset;
auio.uio_resid = nbytes;
auio.uio_rw = UIO_READ;
auio.uio_segflg = seg;
auio.uio_td = curthread;
count = nbytes;
error = fo_read(fp, &auio, fp->f_cred, O_FOFFSET);
if (error) {
if (auio.uio_resid != nbytes && (error == ERESTART || error == EINTR ||
error == EWOULDBLOCK)
) {
error = 0;
}
}
count -= auio.uio_resid;
if (res)
*res = count;
return(error);
}
int
fp_read(file_t fp, void *buf, size_t nbytes, ssize_t *res, int all,
enum uio_seg seg)
{
struct uio auio;
struct iovec aiov;
int error;
int lastresid;
if (res)
*res = 0;
if (nbytes > LONG_MAX)
return (EINVAL);
bzero(&auio, sizeof(auio));
aiov.iov_base = (caddr_t)buf;
aiov.iov_len = nbytes;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_offset = 0;
auio.uio_resid = nbytes;
auio.uio_rw = UIO_READ;
auio.uio_segflg = seg;
auio.uio_td = curthread;
do {
lastresid = auio.uio_resid;
error = fo_read(fp, &auio, fp->f_cred, 0);
} while (all && auio.uio_resid &&
((error == 0 && auio.uio_resid != lastresid) ||
error == ERESTART || error == EINTR));
if (all && error == 0 && auio.uio_resid)
error = ESPIPE;
if (error) {
if (auio.uio_resid != nbytes) {
if (error == ERESTART || error == EINTR)
error = 0;
if (error == EWOULDBLOCK && all == 0)
error = 0;
}
}
if (res)
*res = nbytes - auio.uio_resid;
return(error);
}
int
fp_pwrite(file_t fp, void *buf, size_t nbytes, off_t offset, ssize_t *res,
enum uio_seg seg)
{
struct uio auio;
struct iovec aiov;
size_t count;
int error;
if (res)
*res = 0;
if (nbytes > LONG_MAX)
return (EINVAL);
bzero(&auio, sizeof(auio));
aiov.iov_base = (caddr_t)buf;
aiov.iov_len = nbytes;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_offset = offset;
auio.uio_resid = nbytes;
auio.uio_rw = UIO_WRITE;
auio.uio_segflg = seg;
auio.uio_td = curthread;
count = nbytes;
error = fo_write(fp, &auio, fp->f_cred, O_FOFFSET);
if (error) {
if (auio.uio_resid != nbytes && (error == ERESTART || error == EINTR ||
error == EWOULDBLOCK)
) {
error = 0;
}
}
count -= auio.uio_resid;
if (res)
*res = count;
return(error);
}
int
fp_write(file_t fp, void *buf, size_t nbytes, ssize_t *res, enum uio_seg seg)
{
struct uio auio;
struct iovec aiov;
size_t count;
int error;
if (res)
*res = 0;
if (nbytes > LONG_MAX)
return (EINVAL);
bzero(&auio, sizeof(auio));
aiov.iov_base = (caddr_t)buf;
aiov.iov_len = nbytes;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_offset = 0;
auio.uio_resid = nbytes;
auio.uio_rw = UIO_WRITE;
auio.uio_segflg = seg;
auio.uio_td = curthread;
count = nbytes;
error = fo_write(fp, &auio, fp->f_cred, 0);
if (error) {
if (auio.uio_resid != nbytes && (error == ERESTART || error == EINTR ||
error == EWOULDBLOCK)
) {
error = 0;
}
}
count -= auio.uio_resid;
if (res)
*res = count;
return(error);
}
int
fp_stat(file_t fp, struct stat *ub)
{
int error;
error = fo_stat(fp, ub, fp->f_cred);
return(error);
}
int
fp_mmap(void *addr_arg, size_t size, int prot, int flags, struct file *fp,
off_t pos, void **resp)
{
struct thread *td = curthread;
struct proc *p = td->td_proc;
vm_size_t pageoff;
vm_prot_t maxprot;
vm_offset_t addr;
void *handle;
int error;
vm_object_t obj;
struct vmspace *vms = p->p_vmspace;
struct vnode *vp;
prot &= VM_PROT_ALL;
if ((ssize_t)size < 0 || (flags & MAP_ANON))
return(EINVAL);
pageoff = (pos & PAGE_MASK);
pos -= pageoff;
size += pageoff;
size = (vm_size_t)round_page(size);
addr = (vm_offset_t)addr_arg;
if (flags & MAP_FIXED) {
addr -= pageoff;
if (addr & PAGE_MASK)
return (EINVAL);
if (VM_MAX_USER_ADDRESS > 0 && addr + size > VM_MAX_USER_ADDRESS)
return (EINVAL);
if (VM_MIN_USER_ADDRESS > 0 && addr < VM_MIN_USER_ADDRESS)
return (EINVAL);
if (addr + size < addr)
return (EINVAL);
} else if (addr == 0 ||
(addr >= round_page((vm_offset_t)vms->vm_taddr) &&
addr < round_page((vm_offset_t)vms->vm_daddr + maxdsiz))
) {
addr = round_page((vm_offset_t)vms->vm_daddr + maxdsiz);
}
if (fp->f_type != DTYPE_VNODE)
return (EINVAL);
if (fp->f_flag & FPOSIXSHM)
flags |= MAP_NOSYNC;
vp = (struct vnode *) fp->f_data;
if (vp->v_type != VREG && vp->v_type != VCHR)
return (EINVAL);
if (vp->v_type == VREG) {
if ((obj = vp->v_object) == NULL)
return (EINVAL);
KKASSERT(vp == (struct vnode *)obj->handle);
}
if (vp->v_type == VCHR && iszerodev(vp->v_rdev)) {
handle = NULL;
maxprot = VM_PROT_ALL;
flags |= MAP_ANON;
pos = 0;
} else {
if (vp->v_type == VCHR &&
(flags & (MAP_PRIVATE|MAP_COPY))) {
error = EINVAL;
goto done;
}
maxprot = VM_PROT_EXECUTE;
if (fp->f_flag & FREAD) {
maxprot |= VM_PROT_READ;
} else if (prot & PROT_READ) {
error = EACCES;
goto done;
}
if ((flags & MAP_SHARED) != 0 ||
(vp->v_type == VCHR)
) {
if ((fp->f_flag & FWRITE) != 0) {
struct vattr va;
if ((error = VOP_GETATTR_FP(vp, &va, fp))) {
goto done;
}
if ((va.va_flags & (IMMUTABLE|APPEND)) == 0) {
maxprot |= VM_PROT_WRITE;
} else if (prot & PROT_WRITE) {
error = EPERM;
goto done;
}
} else if ((prot & PROT_WRITE) != 0) {
error = EACCES;
goto done;
}
} else {
maxprot |= VM_PROT_WRITE;
}
handle = (void *)vp;
}
error = vm_mmap(&vms->vm_map, &addr, size, prot,
maxprot, flags, handle, pos, fp);
if (error == 0 && addr_arg)
*resp = (void *)addr;
done:
return (error);
}
int
fp_close(file_t fp)
{
return(fdrop(fp));
}
int
fp_shutdown(file_t fp, int how)
{
return(fo_shutdown(fp, how));
}