Changeset View
Changeset View
Standalone View
Standalone View
head/sys/fs/pseudofs/pseudofs_vnops.c
Show First 20 Lines • Show All 617 Lines • ▼ Show 20 Lines | pfs_open(struct vop_open_args *va) | ||||
/* we don't support locking */ | /* we don't support locking */ | ||||
if ((mode & O_SHLOCK) || (mode & O_EXLOCK)) | if ((mode & O_SHLOCK) || (mode & O_EXLOCK)) | ||||
PFS_RETURN (EOPNOTSUPP); | PFS_RETURN (EOPNOTSUPP); | ||||
PFS_RETURN (0); | PFS_RETURN (0); | ||||
} | } | ||||
struct sbuf_seek_helper { | |||||
off_t skip_bytes; | |||||
struct uio *uio; | |||||
}; | |||||
static int | |||||
pfs_sbuf_uio_drain(void *arg, const char *data, int len) | |||||
{ | |||||
struct sbuf_seek_helper *ssh; | |||||
struct uio *uio; | |||||
int error, skipped; | |||||
ssh = arg; | |||||
uio = ssh->uio; | |||||
skipped = 0; | |||||
/* Need to discard first uio_offset bytes. */ | |||||
if (ssh->skip_bytes > 0) { | |||||
if (ssh->skip_bytes >= len) { | |||||
ssh->skip_bytes -= len; | |||||
return (len); | |||||
} | |||||
data += ssh->skip_bytes; | |||||
len -= ssh->skip_bytes; | |||||
skipped = ssh->skip_bytes; | |||||
ssh->skip_bytes = 0; | |||||
} | |||||
error = uiomove(__DECONST(void *, data), len, uio); | |||||
if (error != 0) | |||||
return (-error); | |||||
/* | /* | ||||
* The fill function has more to emit, but the reader is finished. | |||||
* This is similar to the truncated read case for non-draining PFS | |||||
* sbufs, and should be handled appropriately in fill-routines. | |||||
*/ | |||||
if (uio->uio_resid == 0) | |||||
return (-ENOBUFS); | |||||
return (skipped + len); | |||||
} | |||||
/* | |||||
* Read from a file | * Read from a file | ||||
*/ | */ | ||||
static int | static int | ||||
pfs_read(struct vop_read_args *va) | pfs_read(struct vop_read_args *va) | ||||
{ | { | ||||
struct vnode *vn = va->a_vp; | struct vnode *vn = va->a_vp; | ||||
struct pfs_vdata *pvd = vn->v_data; | struct pfs_vdata *pvd = vn->v_data; | ||||
struct pfs_node *pn = pvd->pvd_pn; | struct pfs_node *pn = pvd->pvd_pn; | ||||
struct uio *uio = va->a_uio; | struct uio *uio = va->a_uio; | ||||
struct proc *proc; | struct proc *proc; | ||||
struct sbuf *sb = NULL; | struct sbuf *sb = NULL; | ||||
int error, locked; | int error, locked; | ||||
off_t buflen; | off_t buflen, buflim; | ||||
struct sbuf_seek_helper ssh; | |||||
PFS_TRACE(("%s", pn->pn_name)); | PFS_TRACE(("%s", pn->pn_name)); | ||||
pfs_assert_not_owned(pn); | pfs_assert_not_owned(pn); | ||||
if (vn->v_type != VREG) | if (vn->v_type != VREG) | ||||
PFS_RETURN (EINVAL); | PFS_RETURN (EINVAL); | ||||
KASSERT_PN_IS_FILE(pn); | KASSERT_PN_IS_FILE(pn); | ||||
Show All 25 Lines | if (pn->pn_flags & PFS_RAWRD) { | ||||
goto ret; | goto ret; | ||||
} | } | ||||
if (uio->uio_resid < 0 || uio->uio_offset < 0 || | if (uio->uio_resid < 0 || uio->uio_offset < 0 || | ||||
uio->uio_resid > OFF_MAX - uio->uio_offset) { | uio->uio_resid > OFF_MAX - uio->uio_offset) { | ||||
error = EINVAL; | error = EINVAL; | ||||
goto ret; | goto ret; | ||||
} | } | ||||
buflen = uio->uio_offset + uio->uio_resid; | buflen = uio->uio_offset + uio->uio_resid + 1; | ||||
if (buflen > PFS_MAXBUFSIZ) | if (pn->pn_flags & PFS_AUTODRAIN) | ||||
buflen = PFS_MAXBUFSIZ; | /* | ||||
* We can use a smaller buffer if we can stream output to the | |||||
* consumer. | |||||
*/ | |||||
buflim = PAGE_SIZE; | |||||
else | |||||
buflim = PFS_MAXBUFSIZ; | |||||
if (buflen > buflim) | |||||
buflen = buflim; | |||||
sb = sbuf_new(sb, NULL, buflen + 1, 0); | sb = sbuf_new(sb, NULL, buflen, 0); | ||||
if (sb == NULL) { | if (sb == NULL) { | ||||
error = EIO; | error = EIO; | ||||
goto ret; | goto ret; | ||||
} | } | ||||
if (pn->pn_flags & PFS_AUTODRAIN) { | |||||
ssh.skip_bytes = uio->uio_offset; | |||||
ssh.uio = uio; | |||||
sbuf_set_drain(sb, pfs_sbuf_uio_drain, &ssh); | |||||
} | |||||
error = pn_fill(curthread, proc, pn, sb, uio); | error = pn_fill(curthread, proc, pn, sb, uio); | ||||
if (error) { | if (error) { | ||||
sbuf_delete(sb); | sbuf_delete(sb); | ||||
goto ret; | goto ret; | ||||
} | } | ||||
/* | /* | ||||
* XXX: If the buffer overflowed, sbuf_len() will not return | * XXX: If the buffer overflowed, sbuf_len() will not return | ||||
* the data length. Then just use the full length because an | * the data length. Then just use the full length because an | ||||
* overflowed sbuf must be full. | * overflowed sbuf must be full. | ||||
*/ | */ | ||||
if (sbuf_finish(sb) == 0) | error = sbuf_finish(sb); | ||||
if ((pn->pn_flags & PFS_AUTODRAIN)) { | |||||
/* | |||||
* ENOBUFS just indicates early termination of the fill | |||||
* function as the caller's buffer was already filled. Squash | |||||
* to zero. | |||||
*/ | |||||
if (uio->uio_resid == 0 && error == ENOBUFS) | |||||
error = 0; | |||||
} else { | |||||
if (error == 0) | |||||
buflen = sbuf_len(sb); | buflen = sbuf_len(sb); | ||||
else | |||||
/* The trailing byte is not valid. */ | |||||
buflen--; | |||||
error = uiomove_frombuf(sbuf_data(sb), buflen, uio); | error = uiomove_frombuf(sbuf_data(sb), buflen, uio); | ||||
} | |||||
sbuf_delete(sb); | sbuf_delete(sb); | ||||
ret: | ret: | ||||
vn_lock(vn, locked | LK_RETRY); | vn_lock(vn, locked | LK_RETRY); | ||||
vdrop(vn); | vdrop(vn); | ||||
if (proc != NULL) | if (proc != NULL) | ||||
PRELE(proc); | PRELE(proc); | ||||
PFS_RETURN (error); | PFS_RETURN (error); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 383 Lines • Show Last 20 Lines |