#include <sys/param.h>
#include <sys/t_lock.h>
#include <sys/systm.h>
#include <sys/sysmacros.h>
#include <sys/user.h>
#include <sys/buf.h>
#include <sys/stat.h>
#include <sys/vfs.h>
#include <sys/vfs_opreg.h>
#include <sys/dirent.h>
#include <sys/vnode.h>
#include <sys/proc.h>
#include <sys/file.h>
#include <sys/fcntl.h>
#include <sys/uio.h>
#include <sys/fs/pc_label.h>
#include <sys/fs/pc_fs.h>
#include <sys/fs/pc_dir.h>
#include <sys/fs/pc_node.h>
#include <sys/mman.h>
#include <sys/pathname.h>
#include <sys/vmsystm.h>
#include <sys/cmn_err.h>
#include <sys/debug.h>
#include <sys/statvfs.h>
#include <sys/unistd.h>
#include <sys/kmem.h>
#include <sys/conf.h>
#include <sys/flock.h>
#include <sys/policy.h>
#include <sys/sdt.h>
#include <sys/sunddi.h>
#include <sys/types.h>
#include <sys/errno.h>
#include <vm/seg.h>
#include <vm/page.h>
#include <vm/pvn.h>
#include <vm/seg_map.h>
#include <vm/seg_vn.h>
#include <vm/hat.h>
#include <vm/as.h>
#include <vm/seg_kmem.h>
#include <fs/fs_subr.h>
static int pcfs_open(struct vnode **, int, struct cred *, caller_context_t *ct);
static int pcfs_close(struct vnode *, int, int, offset_t, struct cred *,
caller_context_t *ct);
static int pcfs_read(struct vnode *, struct uio *, int, struct cred *,
caller_context_t *);
static int pcfs_write(struct vnode *, struct uio *, int, struct cred *,
caller_context_t *);
static int pcfs_getattr(struct vnode *, struct vattr *, int, struct cred *,
caller_context_t *ct);
static int pcfs_setattr(struct vnode *, struct vattr *, int, struct cred *,
caller_context_t *);
static int pcfs_access(struct vnode *, int, int, struct cred *,
caller_context_t *ct);
static int pcfs_lookup(struct vnode *, char *, struct vnode **,
struct pathname *, int, struct vnode *, struct cred *,
caller_context_t *, int *, pathname_t *);
static int pcfs_create(struct vnode *, char *, struct vattr *,
enum vcexcl, int mode, struct vnode **, struct cred *, int,
caller_context_t *, vsecattr_t *);
static int pcfs_remove(struct vnode *, char *, struct cred *,
caller_context_t *, int);
static int pcfs_rename(struct vnode *, char *, struct vnode *, char *,
struct cred *, caller_context_t *, int);
static int pcfs_mkdir(struct vnode *, char *, struct vattr *, struct vnode **,
struct cred *, caller_context_t *, int, vsecattr_t *);
static int pcfs_rmdir(struct vnode *, char *, struct vnode *, struct cred *,
caller_context_t *, int);
static int pcfs_readdir(struct vnode *, struct uio *, struct cred *, int *,
caller_context_t *, int);
static int pcfs_fsync(struct vnode *, int, struct cred *, caller_context_t *);
static void pcfs_inactive(struct vnode *, struct cred *, caller_context_t *);
static int pcfs_fid(struct vnode *vp, struct fid *fidp, caller_context_t *);
static int pcfs_space(struct vnode *, int, struct flock64 *, int,
offset_t, cred_t *, caller_context_t *);
static int pcfs_getpage(struct vnode *, offset_t, size_t, uint_t *, page_t *[],
size_t, struct seg *, caddr_t, enum seg_rw, struct cred *,
caller_context_t *);
static int pcfs_getapage(struct vnode *, u_offset_t, size_t, uint_t *,
page_t *[], size_t, struct seg *, caddr_t, enum seg_rw, struct cred *);
static int pcfs_putpage(struct vnode *, offset_t, size_t, int, struct cred *,
caller_context_t *);
static int pcfs_map(struct vnode *, offset_t, struct as *, caddr_t *, size_t,
uchar_t, uchar_t, uint_t, struct cred *, caller_context_t *);
static int pcfs_addmap(struct vnode *, offset_t, struct as *, caddr_t,
size_t, uchar_t, uchar_t, uint_t, struct cred *, caller_context_t *);
static int pcfs_delmap(struct vnode *, offset_t, struct as *, caddr_t,
size_t, uint_t, uint_t, uint_t, struct cred *, caller_context_t *);
static int pcfs_seek(struct vnode *, offset_t, offset_t *,
caller_context_t *);
static int pcfs_pathconf(struct vnode *, int, ulong_t *, struct cred *,
caller_context_t *);
int pcfs_putapage(struct vnode *, page_t *, u_offset_t *, size_t *, int,
struct cred *);
static int rwpcp(struct pcnode *, struct uio *, enum uio_rw, int);
static int get_long_fn_chunk(struct pcdir_lfn *ep, char *buf);
extern krwlock_t pcnodes_lock;
#define lround(r) (((r)+sizeof (long long)-1)&(~(sizeof (long long)-1)))
struct vnodeops *pcfs_fvnodeops;
struct vnodeops *pcfs_dvnodeops;
const fs_operation_def_t pcfs_fvnodeops_template[] = {
VOPNAME_OPEN, { .vop_open = pcfs_open },
VOPNAME_CLOSE, { .vop_close = pcfs_close },
VOPNAME_READ, { .vop_read = pcfs_read },
VOPNAME_WRITE, { .vop_write = pcfs_write },
VOPNAME_GETATTR, { .vop_getattr = pcfs_getattr },
VOPNAME_SETATTR, { .vop_setattr = pcfs_setattr },
VOPNAME_ACCESS, { .vop_access = pcfs_access },
VOPNAME_FSYNC, { .vop_fsync = pcfs_fsync },
VOPNAME_INACTIVE, { .vop_inactive = pcfs_inactive },
VOPNAME_FID, { .vop_fid = pcfs_fid },
VOPNAME_SEEK, { .vop_seek = pcfs_seek },
VOPNAME_SPACE, { .vop_space = pcfs_space },
VOPNAME_GETPAGE, { .vop_getpage = pcfs_getpage },
VOPNAME_PUTPAGE, { .vop_putpage = pcfs_putpage },
VOPNAME_MAP, { .vop_map = pcfs_map },
VOPNAME_ADDMAP, { .vop_addmap = pcfs_addmap },
VOPNAME_DELMAP, { .vop_delmap = pcfs_delmap },
VOPNAME_PATHCONF, { .vop_pathconf = pcfs_pathconf },
VOPNAME_VNEVENT, { .vop_vnevent = fs_vnevent_support },
NULL, NULL
};
const fs_operation_def_t pcfs_dvnodeops_template[] = {
VOPNAME_OPEN, { .vop_open = pcfs_open },
VOPNAME_CLOSE, { .vop_close = pcfs_close },
VOPNAME_GETATTR, { .vop_getattr = pcfs_getattr },
VOPNAME_SETATTR, { .vop_setattr = pcfs_setattr },
VOPNAME_ACCESS, { .vop_access = pcfs_access },
VOPNAME_LOOKUP, { .vop_lookup = pcfs_lookup },
VOPNAME_CREATE, { .vop_create = pcfs_create },
VOPNAME_REMOVE, { .vop_remove = pcfs_remove },
VOPNAME_RENAME, { .vop_rename = pcfs_rename },
VOPNAME_MKDIR, { .vop_mkdir = pcfs_mkdir },
VOPNAME_RMDIR, { .vop_rmdir = pcfs_rmdir },
VOPNAME_READDIR, { .vop_readdir = pcfs_readdir },
VOPNAME_FSYNC, { .vop_fsync = pcfs_fsync },
VOPNAME_INACTIVE, { .vop_inactive = pcfs_inactive },
VOPNAME_FID, { .vop_fid = pcfs_fid },
VOPNAME_SEEK, { .vop_seek = pcfs_seek },
VOPNAME_PATHCONF, { .vop_pathconf = pcfs_pathconf },
VOPNAME_VNEVENT, { .vop_vnevent = fs_vnevent_support },
NULL, NULL
};
static int
pcfs_open(
struct vnode **vpp,
int flag,
struct cred *cr,
caller_context_t *ct)
{
return (0);
}
static int
pcfs_close(
struct vnode *vp,
int flag,
int count,
offset_t offset,
struct cred *cr,
caller_context_t *ct)
{
return (0);
}
static int
pcfs_read(
struct vnode *vp,
struct uio *uiop,
int ioflag,
struct cred *cr,
struct caller_context *ct)
{
struct pcfs *fsp;
struct pcnode *pcp;
int error;
fsp = VFSTOPCFS(vp->v_vfsp);
if (error = pc_verify(fsp))
return (error);
error = pc_lockfs(fsp, 0, 0);
if (error)
return (error);
if ((pcp = VTOPC(vp)) == NULL || pcp->pc_flags & PC_INVAL) {
pc_unlockfs(fsp);
return (EIO);
}
error = rwpcp(pcp, uiop, UIO_READ, ioflag);
if ((fsp->pcfs_vfs->vfs_flag & VFS_RDONLY) == 0) {
pc_mark_acc(fsp, pcp);
}
pc_unlockfs(fsp);
if (error) {
PC_DPRINTF1(1, "pcfs_read: io error = %d\n", error);
}
return (error);
}
static int
pcfs_write(
struct vnode *vp,
struct uio *uiop,
int ioflag,
struct cred *cr,
struct caller_context *ct)
{
struct pcfs *fsp;
struct pcnode *pcp;
int error;
fsp = VFSTOPCFS(vp->v_vfsp);
if (error = pc_verify(fsp))
return (error);
error = pc_lockfs(fsp, 0, 0);
if (error)
return (error);
if ((pcp = VTOPC(vp)) == NULL || pcp->pc_flags & PC_INVAL) {
pc_unlockfs(fsp);
return (EIO);
}
if (ioflag & FAPPEND) {
uiop->uio_loffset = pcp->pc_size;
}
error = rwpcp(pcp, uiop, UIO_WRITE, ioflag);
pcp->pc_flags |= PC_MOD;
pc_mark_mod(fsp, pcp);
if (ioflag & (FSYNC|FDSYNC))
(void) pc_nodeupdate(pcp);
pc_unlockfs(fsp);
if (error) {
PC_DPRINTF1(1, "pcfs_write: io error = %d\n", error);
}
return (error);
}
static int
rwpcp(
struct pcnode *pcp,
struct uio *uio,
enum uio_rw rw,
int ioflag)
{
struct vnode *vp = PCTOV(pcp);
struct pcfs *fsp;
daddr_t bn;
int n;
offset_t off;
caddr_t base;
int mapon, pagecreate;
int newpage;
int error = 0;
rlim64_t limit = uio->uio_llimit;
int oresid = uio->uio_resid;
if (vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
return (EIO);
PC_DPRINTF4(5, "rwpcp pcp=%p off=%lld resid=%ld size=%u\n", (void *)pcp,
uio->uio_loffset, uio->uio_resid, pcp->pc_size);
ASSERT(rw == UIO_READ || rw == UIO_WRITE);
ASSERT(vp->v_type == VREG);
if (uio->uio_loffset >= UINT32_MAX && rw == UIO_READ) {
return (0);
}
if (uio->uio_loffset < 0)
return (EINVAL);
if (limit == RLIM64_INFINITY || limit > MAXOFFSET_T)
limit = MAXOFFSET_T;
if (uio->uio_loffset >= limit && rw == UIO_WRITE) {
proc_t *p = ttoproc(curthread);
mutex_enter(&p->p_lock);
(void) rctl_action(rctlproc_legacy[RLIMIT_FSIZE], p->p_rctls,
p, RCA_UNSAFE_SIGINFO);
mutex_exit(&p->p_lock);
return (EFBIG);
}
if (uio->uio_loffset >= UINT32_MAX)
return (EFBIG);
if (uio->uio_resid == 0)
return (0);
if (limit > UINT32_MAX)
limit = UINT32_MAX;
fsp = VFSTOPCFS(vp->v_vfsp);
if (fsp->pcfs_flags & PCFS_IRRECOV)
return (EIO);
do {
off = uio->uio_loffset & MAXBMASK;
mapon = (int)(uio->uio_loffset & MAXBOFFSET);
n = MIN(MAXBSIZE - mapon, uio->uio_resid);
if (rw == UIO_READ) {
offset_t diff;
diff = pcp->pc_size - uio->uio_loffset;
if (diff <= 0)
return (0);
if (diff < n)
n = (int)diff;
}
if (rw == UIO_WRITE && uio->uio_loffset + n >= limit) {
if (uio->uio_loffset >= limit) {
error = EFBIG;
break;
}
n = (int)(limit - uio->uio_loffset);
}
uio_prefaultpages((long)n, uio);
base = segmap_getmap(segkmap, vp, (u_offset_t)off);
pagecreate = 0;
newpage = 0;
if (rw == UIO_WRITE) {
if (uio->uio_loffset + n > pcp->pc_size) {
uint_t ncl, lcn;
ncl = (uint_t)howmany((offset_t)pcp->pc_size,
fsp->pcfs_clsize);
if (uio->uio_loffset > pcp->pc_size &&
ncl < (uint_t)howmany(uio->uio_loffset,
fsp->pcfs_clsize)) {
lcn = pc_lblkno(fsp, uio->uio_loffset);
error = pc_balloc(pcp, (daddr_t)lcn,
1, &bn);
ncl = lcn + 1;
}
if (!error &&
ncl < (uint_t)howmany(uio->uio_loffset + n,
fsp->pcfs_clsize))
error = pc_balloc(pcp,
(daddr_t)pc_lblkno(fsp,
uio->uio_loffset + n - 1),
0, &bn);
pcp->pc_flags |= PC_CHG;
if (error) {
pc_cluster32_t ncl;
int nerror;
nerror = pc_fileclsize(fsp,
pcp->pc_scluster, &ncl);
if (nerror) {
PC_DPRINTF1(2,
"cluster chain "
"corruption, "
"scluster=%d\n",
pcp->pc_scluster);
pcp->pc_size = 0;
pcp->pc_flags |= PC_INVAL;
error = nerror;
(void) segmap_release(segkmap,
base, 0);
break;
}
pcp->pc_size = fsp->pcfs_clsize * ncl;
if (error == ENOSPC &&
(pcp->pc_size - uio->uio_loffset)
> 0) {
PC_DPRINTF3(2, "rwpcp ENOSPC "
"off=%lld n=%d size=%d\n",
uio->uio_loffset,
n, pcp->pc_size);
n = (int)(pcp->pc_size -
uio->uio_loffset);
} else {
PC_DPRINTF1(1,
"rwpcp error1=%d\n", error);
(void) segmap_release(segkmap,
base, 0);
break;
}
} else {
pcp->pc_size =
(uint_t)(uio->uio_loffset + n);
}
if (mapon == 0) {
newpage = segmap_pagecreate(segkmap,
base, (size_t)n, 0);
pagecreate = 1;
}
} else if (n == MAXBSIZE) {
newpage = segmap_pagecreate(segkmap, base,
(size_t)n, 0);
pagecreate = 1;
}
}
error = uiomove(base + mapon, (size_t)n, rw, uio);
if (pagecreate && uio->uio_loffset <
roundup(off + mapon + n, PAGESIZE)) {
offset_t nzero, nmoved;
nmoved = uio->uio_loffset - (off + mapon);
nzero = roundup(mapon + n, PAGESIZE) - nmoved;
(void) kzero(base + mapon + nmoved, (size_t)nzero);
}
if (newpage) {
segmap_pageunlock(segkmap, base, (size_t)n,
rw == UIO_WRITE ? S_WRITE : S_READ);
}
if (error) {
PC_DPRINTF1(1, "rwpcp error2=%d\n", error);
if (rw == UIO_WRITE)
(void) segmap_release(segkmap, base, SM_INVAL);
else
(void) segmap_release(segkmap, base, 0);
} else {
uint_t flags = 0;
if (rw == UIO_READ) {
if (n + mapon == MAXBSIZE ||
uio->uio_loffset == pcp->pc_size)
flags = SM_DONTNEED;
} else if (ioflag & (FSYNC|FDSYNC)) {
flags = SM_WRITE;
} else if (n + mapon == MAXBSIZE) {
flags = SM_WRITE|SM_ASYNC|SM_DONTNEED;
}
error = segmap_release(segkmap, base, flags);
}
} while (error == 0 && uio->uio_resid > 0 && n != 0);
if (oresid != uio->uio_resid)
error = 0;
return (error);
}
static int
pcfs_getattr(
struct vnode *vp,
struct vattr *vap,
int flags,
struct cred *cr,
caller_context_t *ct)
{
struct pcnode *pcp;
struct pcfs *fsp;
int error;
char attr;
struct pctime atime;
int64_t unixtime;
PC_DPRINTF1(8, "pcfs_getattr: vp=%p\n", (void *)vp);
fsp = VFSTOPCFS(vp->v_vfsp);
error = pc_lockfs(fsp, 0, 0);
if (error)
return (error);
if ((pcp = VTOPC(vp)) == NULL) {
pc_unlockfs(fsp);
return (EIO);
}
vap->va_type = vp->v_type;
attr = pcp->pc_entry.pcd_attr;
if (PCA_IS_HIDDEN(fsp, attr))
vap->va_mode = 0;
else if (attr & PCA_LABEL)
vap->va_mode = 0444;
else if (attr & PCA_RDONLY)
vap->va_mode = 0555;
else if (fsp->pcfs_flags & PCFS_BOOTPART) {
vap->va_mode = 0755;
} else {
vap->va_mode = 0777;
}
if (attr & PCA_DIR)
vap->va_mode |= S_IFDIR;
else
vap->va_mode |= S_IFREG;
if (fsp->pcfs_flags & PCFS_BOOTPART) {
vap->va_uid = 0;
vap->va_gid = 0;
} else {
vap->va_uid = crgetuid(cr);
vap->va_gid = crgetgid(cr);
}
vap->va_fsid = vp->v_vfsp->vfs_dev;
vap->va_nodeid = (ino64_t)pc_makenodeid(pcp->pc_eblkno,
pcp->pc_eoffset, pcp->pc_entry.pcd_attr,
pc_getstartcluster(fsp, &pcp->pc_entry), pc_direntpersec(fsp));
vap->va_nlink = 1;
vap->va_size = (u_offset_t)pcp->pc_size;
vap->va_rdev = 0;
vap->va_nblocks =
(fsblkcnt64_t)howmany((offset_t)pcp->pc_size, DEV_BSIZE);
vap->va_blksize = fsp->pcfs_clsize;
if (vp->v_flag & VROOT) {
vap->va_mtime = fsp->pcfs_mounttime;
vap->va_atime = fsp->pcfs_mounttime;
vap->va_ctime = fsp->pcfs_mounttime;
pc_unlockfs(fsp);
return (0);
}
pc_pcttotv(&pcp->pc_entry.pcd_mtime, &unixtime);
if ((fsp->pcfs_flags & PCFS_NOCLAMPTIME) == 0) {
if (unixtime > INT32_MAX)
DTRACE_PROBE1(pcfs__mtimeclamped, int64_t, unixtime);
unixtime = MIN(unixtime, INT32_MAX);
} else if (unixtime > INT32_MAX &&
get_udatamodel() == DATAMODEL_ILP32) {
pc_unlockfs(fsp);
DTRACE_PROBE1(pcfs__mtimeoverflowed, int64_t, unixtime);
return (EOVERFLOW);
}
vap->va_mtime.tv_sec = (time_t)unixtime;
vap->va_mtime.tv_nsec = 0;
vap->va_ctime = vap->va_mtime;
atime.pct_date = pcp->pc_entry.pcd_ladate;
if (atime.pct_date == pcp->pc_entry.pcd_mtime.pct_date)
atime.pct_time = pcp->pc_entry.pcd_mtime.pct_time;
else
atime.pct_time = 0;
pc_pcttotv(&atime, &unixtime);
if ((fsp->pcfs_flags & PCFS_NOCLAMPTIME) == 0) {
if (unixtime > INT32_MAX)
DTRACE_PROBE1(pcfs__atimeclamped, int64_t, unixtime);
unixtime = MIN(unixtime, INT32_MAX);
} else if (unixtime > INT32_MAX &&
get_udatamodel() == DATAMODEL_ILP32) {
pc_unlockfs(fsp);
DTRACE_PROBE1(pcfs__atimeoverflowed, int64_t, unixtime);
return (EOVERFLOW);
}
vap->va_atime.tv_sec = (time_t)unixtime;
vap->va_atime.tv_nsec = 0;
pc_unlockfs(fsp);
return (0);
}
static int
pcfs_setattr(
struct vnode *vp,
struct vattr *vap,
int flags,
struct cred *cr,
caller_context_t *ct)
{
struct pcnode *pcp;
mode_t mask = vap->va_mask;
int error;
struct pcfs *fsp;
timestruc_t now, *timep;
PC_DPRINTF2(6, "pcfs_setattr: vp=%p mask=%x\n", (void *)vp, (int)mask);
if (mask & (AT_NOSET | AT_UID | AT_GID)) {
return (EINVAL);
}
if ((vp->v_type == VDIR) &&
(mask & AT_SIZE)) {
return (EINVAL);
}
fsp = VFSTOPCFS(vp->v_vfsp);
error = pc_lockfs(fsp, 0, 0);
if (error)
return (error);
if ((pcp = VTOPC(vp)) == NULL || pcp->pc_flags & PC_INVAL) {
pc_unlockfs(fsp);
return (EIO);
}
if (fsp->pcfs_flags & PCFS_BOOTPART) {
if (secpolicy_pcfs_modify_bootpartition(cr) != 0) {
pc_unlockfs(fsp);
return (EACCES);
}
}
if ((mask & AT_MODE) && (vap->va_mode != (mode_t)-1)) {
if ((vap->va_mode & 0222) == 0)
pcp->pc_entry.pcd_attr |= PCA_RDONLY;
else
pcp->pc_entry.pcd_attr &= ~PCA_RDONLY;
pcp->pc_flags |= PC_CHG;
}
if ((mask & AT_SIZE) && (vap->va_size != (u_offset_t)-1)) {
if (pcp->pc_entry.pcd_attr & PCA_RDONLY) {
error = EACCES;
goto out;
}
if (vap->va_size > UINT32_MAX) {
error = EFBIG;
goto out;
}
error = pc_truncate(pcp, (uint_t)vap->va_size);
if (error)
goto out;
if (vap->va_size == 0)
vnevent_truncate(vp, ct);
}
if (mask & (AT_MTIME | AT_CTIME)) {
timep = &vap->va_mtime;
if (vap->va_mtime.tv_sec == -1) {
gethrestime(&now);
timep = &now;
}
if ((fsp->pcfs_flags & PCFS_NOCLAMPTIME) == 0 &&
timep->tv_sec > INT32_MAX) {
error = EOVERFLOW;
goto out;
}
error = pc_tvtopct(timep, &pcp->pc_entry.pcd_mtime);
if (error)
goto out;
pcp->pc_flags |= PC_CHG;
}
if (mask & AT_ATIME) {
struct pctime atime;
timep = &vap->va_atime;
if (vap->va_atime.tv_sec == -1) {
gethrestime(&now);
timep = &now;
}
if ((fsp->pcfs_flags & PCFS_NOCLAMPTIME) == 0 &&
timep->tv_sec > INT32_MAX) {
error = EOVERFLOW;
goto out;
}
error = pc_tvtopct(timep, &atime);
if (error)
goto out;
pcp->pc_entry.pcd_ladate = atime.pct_date;
pcp->pc_flags |= PC_CHG;
}
out:
pc_unlockfs(fsp);
return (error);
}
static int
pcfs_access(
struct vnode *vp,
int mode,
int flags,
struct cred *cr,
caller_context_t *ct)
{
struct pcnode *pcp;
struct pcfs *fsp;
fsp = VFSTOPCFS(vp->v_vfsp);
if ((pcp = VTOPC(vp)) == NULL || pcp->pc_flags & PC_INVAL)
return (EIO);
if ((mode & VWRITE) && (pcp->pc_entry.pcd_attr & PCA_RDONLY))
return (EACCES);
if (fsp->pcfs_flags & PCFS_BOOTPART) {
if ((mode & VWRITE) &&
secpolicy_pcfs_modify_bootpartition(cr) != 0)
return (EACCES);
}
return (0);
}
static int
pcfs_fsync(
struct vnode *vp,
int syncflag,
struct cred *cr,
caller_context_t *ct)
{
struct pcfs *fsp;
struct pcnode *pcp;
int error;
fsp = VFSTOPCFS(vp->v_vfsp);
if (error = pc_verify(fsp))
return (error);
error = pc_lockfs(fsp, 0, 0);
if (error)
return (error);
if ((pcp = VTOPC(vp)) == NULL || pcp->pc_flags & PC_INVAL) {
pc_unlockfs(fsp);
return (EIO);
}
rw_enter(&pcnodes_lock, RW_WRITER);
error = pc_nodesync(pcp);
rw_exit(&pcnodes_lock);
pc_unlockfs(fsp);
return (error);
}
static void
pcfs_inactive(
struct vnode *vp,
struct cred *cr,
caller_context_t *ct)
{
struct pcnode *pcp;
struct pcfs *fsp;
int error;
fsp = VFSTOPCFS(vp->v_vfsp);
error = pc_lockfs(fsp, 0, 1);
if (vp->v_vfsp->vfs_flag & VFS_UNMOUNTED) {
pcp = VTOPC(vp);
if (vn_has_cached_data(vp)) {
(void) pvn_vplist_dirty(vp, (u_offset_t)0,
pcfs_putapage, B_INVAL, (struct cred *)NULL);
}
remque(pcp);
if (error == 0)
pc_unlockfs(fsp);
vn_free(vp);
kmem_free(pcp, sizeof (struct pcnode));
VFS_RELE(PCFSTOVFS(fsp));
return;
}
mutex_enter(&vp->v_lock);
ASSERT(vp->v_count >= 1);
if (vp->v_count > 1) {
VN_RELE_LOCKED(vp);
mutex_exit(&vp->v_lock);
pc_unlockfs(fsp);
return;
}
mutex_exit(&vp->v_lock);
pcp = VTOPC(vp);
if (pcp == NULL || pcp->pc_flags & PC_INVAL) {
if (vn_has_cached_data(vp))
(void) pvn_vplist_dirty(vp, (u_offset_t)0,
pcfs_putapage, B_INVAL | B_TRUNC,
(struct cred *)NULL);
}
if (pcp == NULL) {
vn_free(vp);
} else {
pc_rele(pcp);
}
if (!error)
pc_unlockfs(fsp);
}
static int
pcfs_lookup(
struct vnode *dvp,
char *nm,
struct vnode **vpp,
struct pathname *pnp,
int flags,
struct vnode *rdir,
struct cred *cr,
caller_context_t *ct,
int *direntflags,
pathname_t *realpnp)
{
struct pcfs *fsp;
struct pcnode *pcp;
int error;
if (dvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
return (EIO);
fsp = VFSTOPCFS(dvp->v_vfsp);
if (error = pc_verify(fsp))
return (error);
error = pc_lockfs(fsp, 0, 0);
if (error)
return (error);
if (VTOPC(dvp) == NULL || VTOPC(dvp)->pc_flags & PC_INVAL) {
pc_unlockfs(fsp);
return (EIO);
}
if (*nm == '\0') {
VN_HOLD(dvp);
*vpp = dvp;
pc_unlockfs(fsp);
return (0);
}
error = pc_dirlook(VTOPC(dvp), nm, &pcp);
if (!error) {
*vpp = PCTOV(pcp);
pcp->pc_flags |= PC_EXTERNAL;
}
pc_unlockfs(fsp);
return (error);
}
static int
pcfs_create(
struct vnode *dvp,
char *nm,
struct vattr *vap,
enum vcexcl exclusive,
int mode,
struct vnode **vpp,
struct cred *cr,
int flag,
caller_context_t *ct,
vsecattr_t *vsecp)
{
int error;
struct pcnode *pcp;
struct vnode *vp;
struct pcfs *fsp;
if (vap->va_type == VDIR)
return (EISDIR);
else if (vap->va_type != VREG)
return (EINVAL);
pcp = NULL;
fsp = VFSTOPCFS(dvp->v_vfsp);
error = pc_lockfs(fsp, 0, 0);
if (error)
return (error);
if (VTOPC(dvp) == NULL || VTOPC(dvp)->pc_flags & PC_INVAL) {
pc_unlockfs(fsp);
return (EIO);
}
if (fsp->pcfs_flags & PCFS_BOOTPART) {
if (secpolicy_pcfs_modify_bootpartition(cr) != 0) {
pc_unlockfs(fsp);
return (EACCES);
}
}
if (*nm == '\0') {
VN_HOLD(dvp);
pcp = VTOPC(dvp);
error = EEXIST;
} else {
error = pc_direnter(VTOPC(dvp), nm, vap, &pcp);
}
if (error == EEXIST) {
vp = PCTOV(pcp);
if (exclusive == NONEXCL) {
if (vp->v_type == VDIR) {
error = EISDIR;
} else if (mode) {
error = pcfs_access(PCTOV(pcp), mode, 0,
cr, ct);
} else {
error = 0;
}
}
if (error) {
VN_RELE(PCTOV(pcp));
} else if ((vp->v_type == VREG) && (vap->va_mask & AT_SIZE) &&
(vap->va_size == 0)) {
error = pc_truncate(pcp, 0L);
if (error) {
VN_RELE(PCTOV(pcp));
} else {
vnevent_create(PCTOV(pcp), ct);
}
}
}
if (error) {
pc_unlockfs(fsp);
return (error);
}
*vpp = PCTOV(pcp);
pcp->pc_flags |= PC_EXTERNAL;
pc_unlockfs(fsp);
return (error);
}
static int
pcfs_remove(
struct vnode *vp,
char *nm,
struct cred *cr,
caller_context_t *ct,
int flags)
{
struct pcfs *fsp;
struct pcnode *pcp;
int error;
fsp = VFSTOPCFS(vp->v_vfsp);
if (error = pc_verify(fsp))
return (error);
error = pc_lockfs(fsp, 0, 0);
if (error)
return (error);
if ((pcp = VTOPC(vp)) == NULL || pcp->pc_flags & PC_INVAL) {
pc_unlockfs(fsp);
return (EIO);
}
if (fsp->pcfs_flags & PCFS_BOOTPART) {
if (secpolicy_pcfs_modify_bootpartition(cr) != 0) {
pc_unlockfs(fsp);
return (EACCES);
}
}
error = pc_dirremove(pcp, nm, NULL, VREG, ct);
pc_unlockfs(fsp);
return (error);
}
static int
pcfs_rename(
struct vnode *sdvp,
char *snm,
struct vnode *tdvp,
char *tnm,
struct cred *cr,
caller_context_t *ct,
int flags)
{
struct pcfs *fsp;
struct pcnode *dp;
struct pcnode *tdp;
int error;
fsp = VFSTOPCFS(sdvp->v_vfsp);
if (error = pc_verify(fsp))
return (error);
error = pcfs_access(sdvp, VWRITE, 0, cr, ct);
if (error) {
return (error);
}
error = pc_lockfs(fsp, 0, 0);
if (error)
return (error);
if (((dp = VTOPC(sdvp)) == NULL) || ((tdp = VTOPC(tdvp)) == NULL) ||
(dp->pc_flags & PC_INVAL) || (tdp->pc_flags & PC_INVAL)) {
pc_unlockfs(fsp);
return (EIO);
}
error = pc_rename(dp, tdp, snm, tnm, ct);
pc_unlockfs(fsp);
return (error);
}
static int
pcfs_mkdir(
struct vnode *dvp,
char *nm,
struct vattr *vap,
struct vnode **vpp,
struct cred *cr,
caller_context_t *ct,
int flags,
vsecattr_t *vsecp)
{
struct pcfs *fsp;
struct pcnode *pcp;
int error;
fsp = VFSTOPCFS(dvp->v_vfsp);
if (error = pc_verify(fsp))
return (error);
error = pc_lockfs(fsp, 0, 0);
if (error)
return (error);
if (VTOPC(dvp) == NULL || VTOPC(dvp)->pc_flags & PC_INVAL) {
pc_unlockfs(fsp);
return (EIO);
}
if (fsp->pcfs_flags & PCFS_BOOTPART) {
if (secpolicy_pcfs_modify_bootpartition(cr) != 0) {
pc_unlockfs(fsp);
return (EACCES);
}
}
error = pc_direnter(VTOPC(dvp), nm, vap, &pcp);
if (!error) {
pcp -> pc_flags |= PC_EXTERNAL;
*vpp = PCTOV(pcp);
} else if (error == EEXIST) {
VN_RELE(PCTOV(pcp));
}
pc_unlockfs(fsp);
return (error);
}
static int
pcfs_rmdir(
struct vnode *dvp,
char *nm,
struct vnode *cdir,
struct cred *cr,
caller_context_t *ct,
int flags)
{
struct pcfs *fsp;
struct pcnode *pcp;
int error;
fsp = VFSTOPCFS(dvp -> v_vfsp);
if (error = pc_verify(fsp))
return (error);
if (error = pc_lockfs(fsp, 0, 0))
return (error);
if ((pcp = VTOPC(dvp)) == NULL || pcp->pc_flags & PC_INVAL) {
pc_unlockfs(fsp);
return (EIO);
}
if (fsp->pcfs_flags & PCFS_BOOTPART) {
if (secpolicy_pcfs_modify_bootpartition(cr) != 0) {
pc_unlockfs(fsp);
return (EACCES);
}
}
error = pc_dirremove(pcp, nm, cdir, VDIR, ct);
pc_unlockfs(fsp);
return (error);
}
static int
pcfs_readdir(
struct vnode *dvp,
struct uio *uiop,
struct cred *cr,
int *eofp,
caller_context_t *ct,
int flags)
{
struct pcnode *pcp;
struct pcfs *fsp;
struct pcdir *ep;
struct buf *bp = NULL;
offset_t offset;
int boff;
struct pc_dirent lbp;
struct pc_dirent *ld = &lbp;
int error;
if (dvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
return (EIO);
if ((uiop->uio_iovcnt != 1) ||
(uiop->uio_loffset % sizeof (struct pcdir)) != 0) {
return (EINVAL);
}
fsp = VFSTOPCFS(dvp->v_vfsp);
if (error = pc_verify(fsp)) {
return (error);
}
error = pc_lockfs(fsp, 0, 0);
if (error)
return (error);
if ((pcp = VTOPC(dvp)) == NULL || pcp->pc_flags & PC_INVAL) {
pc_unlockfs(fsp);
return (EIO);
}
bzero(ld, sizeof (*ld));
if (eofp != NULL)
*eofp = 0;
offset = uiop->uio_loffset;
if (dvp->v_flag & VROOT) {
if (offset == 0) {
(void) strcpy(ld->d_name, ".");
ld->d_reclen = DIRENT64_RECLEN(1);
ld->d_off = (off64_t)sizeof (struct pcdir);
ld->d_ino = (ino64_t)UINT_MAX;
if (ld->d_reclen > uiop->uio_resid) {
pc_unlockfs(fsp);
return (ENOSPC);
}
(void) uiomove(ld, ld->d_reclen, UIO_READ, uiop);
uiop->uio_loffset = ld->d_off;
offset = uiop->uio_loffset;
}
if (offset == sizeof (struct pcdir)) {
(void) strcpy(ld->d_name, "..");
ld->d_reclen = DIRENT64_RECLEN(2);
if (ld->d_reclen > uiop->uio_resid) {
pc_unlockfs(fsp);
return (ENOSPC);
}
ld->d_off = (off64_t)(uiop->uio_loffset +
sizeof (struct pcdir));
ld->d_ino = (ino64_t)UINT_MAX;
(void) uiomove(ld, ld->d_reclen, UIO_READ, uiop);
uiop->uio_loffset = ld->d_off;
offset = uiop->uio_loffset;
}
offset -= 2 * sizeof (struct pcdir);
}
for (;;) {
boff = pc_blkoff(fsp, offset);
if (boff == 0 || bp == NULL || boff >= bp->b_bcount) {
if (bp != NULL) {
brelse(bp);
bp = NULL;
}
error = pc_blkatoff(pcp, offset, &bp, &ep);
if (error) {
if (error == ENOENT) {
error = 0;
if (eofp)
*eofp = 1;
}
break;
}
}
if (ep->pcd_filename[0] == PCD_UNUSED) {
if (eofp)
*eofp = 1;
break;
}
if (ep->pcd_filename[0] == PCD_ERASED) {
uiop->uio_loffset += sizeof (struct pcdir);
offset += sizeof (struct pcdir);
ep++;
continue;
}
if (PCDL_IS_LFN(ep)) {
if (pc_read_long_fn(dvp, uiop, ld, &ep, &offset, &bp) !=
0)
break;
continue;
}
if (pc_read_short_fn(dvp, uiop, ld, &ep, &offset, &bp) != 0)
break;
}
if (bp)
brelse(bp);
pc_unlockfs(fsp);
return (error);
}
static int
pcfs_getapage(
struct vnode *vp,
u_offset_t off,
size_t len,
uint_t *protp,
page_t *pl[],
size_t plsz,
struct seg *seg,
caddr_t addr,
enum seg_rw rw,
struct cred *cr)
{
struct pcnode *pcp;
struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
struct vnode *devvp;
page_t *pp;
page_t *pagefound;
int err;
if (vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
return (EIO);
PC_DPRINTF3(5, "pcfs_getapage: vp=%p off=%lld len=%lu\n",
(void *)vp, off, len);
if ((pcp = VTOPC(vp)) == NULL || pcp->pc_flags & PC_INVAL)
return (EIO);
devvp = fsp->pcfs_devvp;
if (pl == NULL)
return (0);
pl[0] = NULL;
err = 0;
if ((pcp->pc_flags & PC_ACC) == 0 &&
((fsp->pcfs_vfs->vfs_flag & VFS_RDONLY) == 0)) {
pc_mark_acc(fsp, pcp);
}
reread:
if ((pagefound = page_exists(vp, off)) == NULL) {
struct buf *bp;
daddr_t lbn, bn;
u_offset_t io_off;
size_t io_len;
u_offset_t lbnoff, xferoffset;
u_offset_t pgoff;
uint_t xfersize;
int err1;
lbn = pc_lblkno(fsp, off);
lbnoff = off & ~(fsp->pcfs_clsize - 1);
xferoffset = off & ~(fsp->pcfs_secsize - 1);
pp = pvn_read_kluster(vp, off, seg, addr, &io_off, &io_len,
off, (size_t)MIN(pc_blksize(fsp, pcp, off), PAGESIZE), 0);
if (pp == NULL)
panic("pcfs_getapage pvn_read_kluster");
for (pgoff = 0; pgoff < PAGESIZE && xferoffset < pcp->pc_size;
pgoff += xfersize,
lbn += howmany(xfersize, fsp->pcfs_clsize),
lbnoff += xfersize, xferoffset += xfersize) {
xfersize = PAGESIZE - pgoff;
err1 = pc_bmap(pcp, lbn, &bn, &xfersize);
if (err1) {
PC_DPRINTF1(1, "pc_getapage err=%d", err1);
err = err1;
goto out;
}
bp = pageio_setup(pp, xfersize, devvp, B_READ);
bp->b_edev = devvp->v_rdev;
bp->b_dev = cmpdev(devvp->v_rdev);
bp->b_blkno = bn + btodt(xferoffset - lbnoff);
bp->b_un.b_addr = (caddr_t)(uintptr_t)pgoff;
bp->b_file = vp;
bp->b_offset = (offset_t)(off + pgoff);
(void) bdev_strategy(bp);
lwp_stat_update(LWP_STAT_INBLK, 1);
if (err == 0)
err = biowait(bp);
else
(void) biowait(bp);
pageio_done(bp);
if (err)
goto out;
}
if (pgoff < PAGESIZE) {
pagezero(pp->p_prev, pgoff, PAGESIZE - pgoff);
}
pvn_plist_init(pp, pl, plsz, off, io_len, rw);
}
out:
if (err) {
if (pp != NULL)
pvn_read_done(pp, B_ERROR);
return (err);
}
if (pagefound) {
if ((pp = page_lookup(vp, off, SE_SHARED)) == NULL) {
goto reread;
}
pl[0] = pp;
pl[1] = NULL;
}
return (err);
}
static int
pcfs_getpage(
struct vnode *vp,
offset_t off,
size_t len,
uint_t *protp,
page_t *pl[],
size_t plsz,
struct seg *seg,
caddr_t addr,
enum seg_rw rw,
struct cred *cr,
caller_context_t *ct)
{
struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
int err;
PC_DPRINTF0(6, "pcfs_getpage\n");
if (err = pc_verify(fsp))
return (err);
if (vp->v_flag & VNOMAP)
return (ENOSYS);
ASSERT(off <= UINT32_MAX);
err = pc_lockfs(fsp, 0, 0);
if (err)
return (err);
if (protp != NULL)
*protp = PROT_ALL;
ASSERT((off & PAGEOFFSET) == 0);
err = pvn_getpages(pcfs_getapage, vp, off, len, protp, pl, plsz,
seg, addr, rw, cr);
pc_unlockfs(fsp);
return (err);
}
static int
pcfs_putpage(
struct vnode *vp,
offset_t off,
size_t len,
int flags,
struct cred *cr,
caller_context_t *ct)
{
struct pcnode *pcp;
page_t *pp;
struct pcfs *fsp;
u_offset_t io_off;
size_t io_len;
offset_t eoff;
int err;
if (vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
return (EIO);
PC_DPRINTF1(6, "pcfs_putpage vp=0x%p\n", (void *)vp);
if (vp->v_flag & VNOMAP)
return (ENOSYS);
fsp = VFSTOPCFS(vp->v_vfsp);
if (err = pc_verify(fsp))
return (err);
if ((pcp = VTOPC(vp)) == NULL) {
PC_DPRINTF1(3, "pcfs_putpage NULL vp=0x%p\n", (void *)vp);
return (EIO);
}
if (pcp->pc_flags & PC_INVAL)
return (EIO);
if (curproc == proc_pageout) {
return (ENOMEM);
}
ASSERT(off <= UINT32_MAX);
flags &= ~B_ASYNC;
err = pc_lockfs(fsp, 0, 0);
if (err)
return (err);
if (!vn_has_cached_data(vp) || off >= pcp->pc_size) {
pc_unlockfs(fsp);
return (0);
}
if (len == 0) {
err = pvn_vplist_dirty(vp, off,
pcfs_putapage, flags, cr);
} else {
eoff = off + len;
for (io_off = off; io_off < eoff &&
io_off < pcp->pc_size; io_off += io_len) {
if ((flags & B_INVAL) || ((flags & B_ASYNC) == 0)) {
pp = page_lookup(vp, io_off,
(flags & (B_INVAL | B_FREE)) ?
SE_EXCL : SE_SHARED);
} else {
pp = page_lookup_nowait(vp, io_off,
(flags & B_FREE) ? SE_EXCL : SE_SHARED);
}
if (pp == NULL || pvn_getdirty(pp, flags) == 0)
io_len = PAGESIZE;
else {
err = pcfs_putapage(vp, pp, &io_off, &io_len,
flags, cr);
if (err != 0)
break;
}
}
}
if (err == 0 && (flags & B_INVAL) &&
off == 0 && len == 0 && vn_has_cached_data(vp)) {
cmn_err(CE_PANIC,
"pcfs_putpage: B_INVAL, pages not gone");
} else if (err) {
PC_DPRINTF1(1, "pcfs_putpage err=%d\n", err);
}
pc_unlockfs(fsp);
return (err);
}
int
pcfs_putapage(
struct vnode *vp,
page_t *pp,
u_offset_t *offp,
size_t *lenp,
int flags,
struct cred *cr)
{
struct pcnode *pcp;
struct pcfs *fsp;
struct vnode *devvp;
size_t io_len;
daddr_t bn;
u_offset_t lbn, lbnoff, xferoffset;
uint_t pgoff, xfersize;
int err = 0;
u_offset_t io_off;
pcp = VTOPC(vp);
fsp = VFSTOPCFS(vp->v_vfsp);
devvp = fsp->pcfs_devvp;
if ((pcp->pc_flags & PC_MOD) == 0 || (flags & B_FORCE)) {
pcp->pc_flags |= PC_MOD;
pc_mark_mod(fsp, pcp);
}
pp = pvn_write_kluster(vp, pp, &io_off, &io_len, pp->p_offset,
PAGESIZE, flags);
if (fsp->pcfs_flags & PCFS_IRRECOV) {
goto out;
}
PC_DPRINTF1(7, "pc_putpage writing dirty page off=%llu\n", io_off);
lbn = pc_lblkno(fsp, io_off);
lbnoff = io_off & ~(fsp->pcfs_clsize - 1);
xferoffset = io_off & ~(fsp->pcfs_secsize - 1);
for (pgoff = 0; pgoff < io_len && xferoffset < pcp->pc_size;
pgoff += xfersize,
lbn += howmany(xfersize, fsp->pcfs_clsize),
lbnoff += xfersize, xferoffset += xfersize) {
struct buf *bp;
int err1;
xfersize = io_len - pgoff;
err1 = pc_bmap(pcp, (daddr_t)lbn, &bn, &xfersize);
if (err1) {
err = err1;
goto out;
}
bp = pageio_setup(pp, xfersize, devvp, B_WRITE | flags);
bp->b_edev = devvp->v_rdev;
bp->b_dev = cmpdev(devvp->v_rdev);
bp->b_blkno = bn + btodt(xferoffset - lbnoff);
bp->b_un.b_addr = (caddr_t)(uintptr_t)pgoff;
bp->b_file = vp;
bp->b_offset = (offset_t)(io_off + pgoff);
(void) bdev_strategy(bp);
lwp_stat_update(LWP_STAT_OUBLK, 1);
if (err == 0)
err = biowait(bp);
else
(void) biowait(bp);
pageio_done(bp);
}
pvn_write_done(pp, ((err) ? B_ERROR : 0) | B_WRITE | flags);
pp = NULL;
out:
if ((fsp->pcfs_flags & PCFS_IRRECOV) && pp != NULL) {
pvn_write_done(pp, B_WRITE | flags);
} else if (err != 0 && pp != NULL) {
pvn_write_done(pp, B_ERROR | B_WRITE | flags);
}
if (offp)
*offp = io_off;
if (lenp)
*lenp = io_len;
PC_DPRINTF4(4, "pcfs_putapage: vp=%p pp=%p off=%lld len=%lu\n",
(void *)vp, (void *)pp, io_off, io_len);
if (err) {
PC_DPRINTF1(1, "pcfs_putapage err=%d", err);
}
return (err);
}
static int
pcfs_map(
struct vnode *vp,
offset_t off,
struct as *as,
caddr_t *addrp,
size_t len,
uchar_t prot,
uchar_t maxprot,
uint_t flags,
struct cred *cr,
caller_context_t *ct)
{
struct segvn_crargs vn_a;
int error;
PC_DPRINTF0(6, "pcfs_map\n");
if (vp->v_flag & VNOMAP)
return (ENOSYS);
if (off > UINT32_MAX || off + len > UINT32_MAX)
return (ENXIO);
as_rangelock(as);
error = choose_addr(as, addrp, len, off, ADDR_VACALIGN, flags);
if (error != 0) {
as_rangeunlock(as);
return (error);
}
vn_a.vp = vp;
vn_a.offset = off;
vn_a.type = flags & MAP_TYPE;
vn_a.prot = prot;
vn_a.maxprot = maxprot;
vn_a.flags = flags & ~MAP_TYPE;
vn_a.cred = cr;
vn_a.amp = NULL;
vn_a.szc = 0;
vn_a.lgrp_mem_policy_flags = 0;
error = as_map(as, *addrp, len, segvn_create, &vn_a);
as_rangeunlock(as);
return (error);
}
static int
pcfs_seek(
struct vnode *vp,
offset_t ooff,
offset_t *noffp,
caller_context_t *ct)
{
if (*noffp < 0)
return (EINVAL);
else if (*noffp > MAXOFFSET_T)
return (EINVAL);
else
return (0);
}
static int
pcfs_addmap(
struct vnode *vp,
offset_t off,
struct as *as,
caddr_t addr,
size_t len,
uchar_t prot,
uchar_t maxprot,
uint_t flags,
struct cred *cr,
caller_context_t *ct)
{
if (vp->v_flag & VNOMAP)
return (ENOSYS);
return (0);
}
static int
pcfs_delmap(
struct vnode *vp,
offset_t off,
struct as *as,
caddr_t addr,
size_t len,
uint_t prot,
uint_t maxprot,
uint_t flags,
struct cred *cr,
caller_context_t *ct)
{
if (vp->v_flag & VNOMAP)
return (ENOSYS);
return (0);
}
static int
pcfs_pathconf(
struct vnode *vp,
int cmd,
ulong_t *valp,
struct cred *cr,
caller_context_t *ct)
{
struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
switch (cmd) {
case _PC_LINK_MAX:
*valp = 1;
return (0);
case _PC_CASE_BEHAVIOR:
return (EINVAL);
case _PC_FILESIZEBITS:
*valp = IS_FAT12(fsp) ? 30 : 33;
return (0);
case _PC_TIMESTAMP_RESOLUTION:
*valp = 2000000000L;
return (0);
default:
return (fs_pathconf(vp, cmd, valp, cr, ct));
}
}
static int
pcfs_space(
struct vnode *vp,
int cmd,
struct flock64 *bfp,
int flag,
offset_t offset,
cred_t *cr,
caller_context_t *ct)
{
struct vattr vattr;
int error;
if (cmd != F_FREESP)
return (EINVAL);
if ((error = convoff(vp, bfp, 0, offset)) == 0) {
if ((bfp->l_start > UINT32_MAX) || (bfp->l_len > UINT32_MAX))
return (EFBIG);
if (bfp->l_len != 0)
return (EINVAL);
vattr.va_mask = AT_SIZE;
vattr.va_size = bfp->l_start;
error = VOP_SETATTR(vp, (vattr_t *)&vattr, 0, cr, ct);
}
return (error);
}
void
set_long_fn_chunk(struct pcdir_lfn *ep, char *buf, int len)
{
int i;
ASSERT(buf != NULL);
for (i = 0; i < PCLF_FIRSTNAMESIZE; i += 2) {
if (len > 0) {
ep->pcdl_firstfilename[i] = *buf++;
ep->pcdl_firstfilename[i + 1] = *buf++;
len -= 2;
} else {
ep->pcdl_firstfilename[i] = (uchar_t)0xff;
ep->pcdl_firstfilename[i + 1] = (uchar_t)0xff;
}
}
for (i = 0; i < PCLF_SECONDNAMESIZE; i += 2) {
if (len > 0) {
ep->pcdl_secondfilename[i] = *buf++;
ep->pcdl_secondfilename[i + 1] = *buf++;
len -= 2;
} else {
ep->pcdl_secondfilename[i] = (uchar_t)0xff;
ep->pcdl_secondfilename[i + 1] = (uchar_t)0xff;
}
}
for (i = 0; i < PCLF_THIRDNAMESIZE; i += 2) {
if (len > 0) {
ep->pcdl_thirdfilename[i] = *buf++;
ep->pcdl_thirdfilename[i + 1] = *buf++;
len -= 2;
} else {
ep->pcdl_thirdfilename[i] = (uchar_t)0xff;
ep->pcdl_thirdfilename[i + 1] = (uchar_t)0xff;
}
}
}
static int
get_long_fn_chunk(struct pcdir_lfn *ep, char *buf)
{
char *tmp = buf;
int i;
for (i = 0; i < PCLF_FIRSTNAMESIZE; i += 2, tmp += 2) {
*tmp = ep->pcdl_firstfilename[i];
*(tmp + 1) = ep->pcdl_firstfilename[i + 1];
if ((*tmp == '\0') && (*(tmp+1) == '\0'))
return (tmp - buf);
}
for (i = 0; i < PCLF_SECONDNAMESIZE; i += 2, tmp += 2) {
*tmp = ep->pcdl_secondfilename[i];
*(tmp + 1) = ep->pcdl_secondfilename[i + 1];
if ((*tmp == '\0') && (*(tmp+1) == '\0'))
return (tmp - buf);
}
for (i = 0; i < PCLF_THIRDNAMESIZE; i += 2, tmp += 2) {
*tmp = ep->pcdl_thirdfilename[i];
*(tmp + 1) = ep->pcdl_thirdfilename[i + 1];
if ((*tmp == '\0') && (*(tmp+1) == '\0'))
return (tmp - buf);
}
return (tmp - buf);
}
uchar_t
pc_checksum_long_fn(char *name, char *ext)
{
uchar_t c;
char b[11];
bcopy(name, b, 8);
bcopy(ext, b+8, 3);
c = b[0];
c = ((c << 7) | (c >> 1)) + b[1];
c = ((c << 7) | (c >> 1)) + b[2];
c = ((c << 7) | (c >> 1)) + b[3];
c = ((c << 7) | (c >> 1)) + b[4];
c = ((c << 7) | (c >> 1)) + b[5];
c = ((c << 7) | (c >> 1)) + b[6];
c = ((c << 7) | (c >> 1)) + b[7];
c = ((c << 7) | (c >> 1)) + b[8];
c = ((c << 7) | (c >> 1)) + b[9];
c = ((c << 7) | (c >> 1)) + b[10];
return (c);
}
int
pc_extract_long_fn(struct pcnode *pcp, char *namep,
struct pcdir **epp, offset_t *offset, struct buf **bp)
{
struct pcdir *ep = *epp;
struct pcdir_lfn *lep = (struct pcdir_lfn *)ep;
struct vnode *dvp = PCTOV(pcp);
struct pcfs *fsp = VFSTOPCFS(dvp->v_vfsp);
char *lfn;
char *lfn_base;
int boff;
int i, cs;
char *buf;
uchar_t cksum;
int detached = 0;
int error = 0;
int foldcase;
int count = 0;
size_t u16l = 0, u8l = 0;
char *outbuf;
size_t ret, inlen, outlen;
foldcase = (fsp->pcfs_flags & PCFS_FOLDCASE);
lfn_base = kmem_alloc(PCMAXNAM_UTF16, KM_SLEEP);
lfn = lfn_base + PCMAXNAM_UTF16 - sizeof (uint16_t);
*lfn = '\0';
*(lfn + 1) = '\0';
cksum = lep->pcdl_checksum;
buf = kmem_alloc(PCMAXNAM_UTF16, KM_SLEEP);
for (i = (lep->pcdl_ordinal & ~0xc0); i > 0; i--) {
boff = pc_blkoff(fsp, *offset);
if (boff == 0 || *bp == NULL || boff >= (*bp)->b_bcount) {
if (*bp != NULL) {
brelse(*bp);
*bp = NULL;
}
error = pc_blkatoff(pcp, *offset, bp, &ep);
if (error) {
kmem_free(lfn_base, PCMAXNAM_UTF16);
kmem_free(buf, PCMAXNAM_UTF16);
return (error);
}
lep = (struct pcdir_lfn *)ep;
}
if (!PCDL_IS_LFN((struct pcdir *)lep)) {
detached = 1;
break;
}
if (cksum != lep->pcdl_checksum)
detached = 1;
cs = get_long_fn_chunk(lep, buf);
count += cs;
for (; cs > 0; cs--) {
if (lfn >= lfn_base)
*--lfn = buf[cs - 1];
else
detached = 1;
}
lep++;
*offset += sizeof (struct pcdir);
}
kmem_free(buf, PCMAXNAM_UTF16);
boff = pc_blkoff(fsp, *offset);
ep = (struct pcdir *)lep;
if (boff == 0 || *bp == NULL || boff >= (*bp)->b_bcount) {
if (*bp != NULL) {
brelse(*bp);
*bp = NULL;
}
error = pc_blkatoff(pcp, *offset, bp, &ep);
if (error) {
kmem_free(lfn_base, PCMAXNAM_UTF16);
return (error);
}
}
if (PCDL_IS_LFN(ep) || ((ep->pcd_filename[0] == PCD_UNUSED) ||
(ep->pcd_filename[0] == PCD_ERASED))) {
detached = 1;
}
if (detached ||
(cksum != pc_checksum_long_fn(ep->pcd_filename, ep->pcd_ext)) ||
!pc_valid_long_fn(lfn, 0)) {
*epp = ep;
kmem_free(lfn_base, PCMAXNAM_UTF16);
return (EINVAL);
}
if (PCA_IS_HIDDEN(fsp, ep->pcd_attr)) {
*offset += sizeof (struct pcdir);
ep++;
*epp = ep;
kmem_free(lfn_base, PCMAXNAM_UTF16);
return (EINVAL);
}
if (namep) {
u16l = count / 2;
u8l = PCMAXNAMLEN;
error = uconv_u16tou8((const uint16_t *)lfn, &u16l,
(uchar_t *)namep, &u8l, UCONV_IN_LITTLE_ENDIAN);
if (error != 0) {
kmem_free(lfn_base, PCMAXNAM_UTF16);
return (EINVAL);
}
namep[u8l] = '\0';
if (foldcase) {
inlen = strlen(namep);
outlen = PCMAXNAMLEN;
outbuf = kmem_alloc(PCMAXNAMLEN + 1, KM_SLEEP);
ret = u8_textprep_str(namep, &inlen, outbuf,
&outlen, U8_TEXTPREP_TOLOWER, U8_UNICODE_LATEST,
&error);
if (ret == -1) {
kmem_free(outbuf, PCMAXNAMLEN + 1);
kmem_free(lfn_base, PCMAXNAM_UTF16);
return (EINVAL);
}
outbuf[PCMAXNAMLEN - outlen] = '\0';
(void) strncpy(namep, outbuf, PCMAXNAMLEN + 1);
kmem_free(outbuf, PCMAXNAMLEN + 1);
}
}
kmem_free(lfn_base, PCMAXNAM_UTF16);
*epp = ep;
return (0);
}
int
pc_read_long_fn(struct vnode *dvp, struct uio *uiop, struct pc_dirent *ld,
struct pcdir **epp, offset_t *offset, struct buf **bp)
{
struct pcdir *ep;
struct pcnode *pcp = VTOPC(dvp);
struct pcfs *fsp = VFSTOPCFS(dvp->v_vfsp);
offset_t uiooffset = uiop->uio_loffset;
int error = 0;
offset_t oldoffset;
oldoffset = *offset;
error = pc_extract_long_fn(pcp, ld->d_name, epp, offset, bp);
if (error) {
if (error == EINVAL) {
uiop->uio_loffset += *offset - oldoffset;
return (0);
} else
return (error);
}
ep = *epp;
uiop->uio_loffset += *offset - oldoffset;
ld->d_reclen = DIRENT64_RECLEN(strlen(ld->d_name));
if (ld->d_reclen > uiop->uio_resid) {
uiop->uio_loffset = uiooffset;
return (ENOSPC);
}
ld->d_off = uiop->uio_loffset + sizeof (struct pcdir);
ld->d_ino = pc_makenodeid(pc_daddrdb(fsp, (*bp)->b_blkno),
pc_blkoff(fsp, *offset), ep->pcd_attr,
pc_getstartcluster(fsp, ep), pc_direntpersec(fsp));
(void) uiomove((caddr_t)ld, ld->d_reclen, UIO_READ, uiop);
uiop->uio_loffset = ld->d_off;
*offset += sizeof (struct pcdir);
ep++;
*epp = ep;
return (0);
}
int
pc_read_short_fn(struct vnode *dvp, struct uio *uiop, struct pc_dirent *ld,
struct pcdir **epp, offset_t *offset, struct buf **bp)
{
struct pcfs *fsp = VFSTOPCFS(dvp->v_vfsp);
int boff = pc_blkoff(fsp, *offset);
struct pcdir *ep = *epp;
offset_t oldoffset = uiop->uio_loffset;
int error;
int foldcase;
if (PCA_IS_HIDDEN(fsp, ep->pcd_attr)) {
uiop->uio_loffset += sizeof (struct pcdir);
*offset += sizeof (struct pcdir);
ep++;
*epp = ep;
return (0);
}
ld->d_ino = (ino64_t)pc_makenodeid(pc_daddrdb(fsp, (*bp)->b_blkno),
boff, ep->pcd_attr, pc_getstartcluster(fsp, ep),
pc_direntpersec(fsp));
foldcase = (fsp->pcfs_flags & PCFS_FOLDCASE);
error = pc_fname_ext_to_name(&ld->d_name[0], &ep->pcd_filename[0],
&ep->pcd_ext[0], foldcase);
if (error == 0) {
ld->d_reclen = DIRENT64_RECLEN(strlen(ld->d_name));
if (ld->d_reclen > uiop->uio_resid) {
uiop->uio_loffset = oldoffset;
return (ENOSPC);
}
ld->d_off = (off64_t)(uiop->uio_loffset +
sizeof (struct pcdir));
(void) uiomove((caddr_t)ld,
ld->d_reclen, UIO_READ, uiop);
uiop->uio_loffset = ld->d_off;
} else {
uiop->uio_loffset += sizeof (struct pcdir);
}
*offset += sizeof (struct pcdir);
ep++;
*epp = ep;
return (0);
}
static int
pcfs_fid(struct vnode *vp, struct fid *fidp, caller_context_t *ct)
{
struct pc_fid *pcfid;
struct pcnode *pcp;
struct pcfs *fsp;
int error;
fsp = VFSTOPCFS(vp->v_vfsp);
if (fsp == NULL)
return (EIO);
error = pc_lockfs(fsp, 0, 0);
if (error)
return (error);
if ((pcp = VTOPC(vp)) == NULL || pcp->pc_flags & PC_INVAL) {
pc_unlockfs(fsp);
return (EIO);
}
if (fidp->fid_len < (sizeof (struct pc_fid) - sizeof (ushort_t))) {
fidp->fid_len = sizeof (struct pc_fid) - sizeof (ushort_t);
pc_unlockfs(fsp);
return (ENOSPC);
}
pcfid = (struct pc_fid *)fidp;
bzero(pcfid, sizeof (struct pc_fid));
pcfid->pcfid_len = sizeof (struct pc_fid) - sizeof (ushort_t);
if (vp->v_flag & VROOT) {
pcfid->pcfid_block = 0;
pcfid->pcfid_offset = 0;
pcfid->pcfid_ctime = 0;
} else {
pcfid->pcfid_block = pcp->pc_eblkno;
pcfid->pcfid_offset = pcp->pc_eoffset;
pcfid->pcfid_ctime = pcp->pc_entry.pcd_crtime.pct_time;
}
pc_unlockfs(fsp);
return (0);
}