#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/uio.h>
#include <sys/errno.h>
#include <sys/pathname.h>
#include <sys/kmem.h>
#include <sys/cred.h>
#include <sys/vnode.h>
#include <sys/debug.h>
void
pn_alloc(struct pathname *pnp)
{
pn_alloc_sz(pnp, MAXPATHLEN);
}
void
pn_alloc_sz(struct pathname *pnp, size_t sz)
{
pnp->pn_path = pnp->pn_buf = kmem_alloc(sz, KM_SLEEP);
pnp->pn_pathlen = 0;
pnp->pn_bufsize = sz;
}
void
pn_free(struct pathname *pnp)
{
kmem_free(pnp->pn_buf, pnp->pn_bufsize);
pnp->pn_path = pnp->pn_buf = NULL;
pnp->pn_pathlen = pnp->pn_bufsize = 0;
}
int
pn_get_buf(const char *str, enum uio_seg seg, struct pathname *pnp,
void *buf, size_t bufsize)
{
int error;
pnp->pn_path = pnp->pn_buf = buf;
pnp->pn_bufsize = bufsize;
if (seg == UIO_USERSPACE)
error = copyinstr(str, pnp->pn_path, bufsize, &pnp->pn_pathlen);
else
error = copystr(str, pnp->pn_path, bufsize, &pnp->pn_pathlen);
if (error)
return (error);
pnp->pn_pathlen--;
return (0);
}
int
pn_get(const char *str, enum uio_seg seg, struct pathname *pnp)
{
int error;
void *buf;
buf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
if ((error = pn_get_buf(str, seg, pnp, buf, MAXPATHLEN)) != 0)
pn_free(pnp);
return (error);
}
int
pn_set(struct pathname *pnp, const char *path)
{
int error;
pnp->pn_path = pnp->pn_buf;
error = copystr(path, pnp->pn_path, pnp->pn_bufsize, &pnp->pn_pathlen);
pnp->pn_pathlen--;
return (error);
}
int
pn_insert(struct pathname *pnp, struct pathname *sympnp, size_t complen)
{
if (*sympnp->pn_path == '/') {
if (pnp->pn_pathlen + sympnp->pn_pathlen >= pnp->pn_bufsize)
return (ENAMETOOLONG);
if (pnp->pn_pathlen != 0)
ovbcopy(pnp->pn_path, pnp->pn_buf + sympnp->pn_pathlen,
pnp->pn_pathlen);
bcopy(sympnp->pn_path, pnp->pn_buf, sympnp->pn_pathlen);
pnp->pn_pathlen += sympnp->pn_pathlen;
pnp->pn_buf[pnp->pn_pathlen] = '\0';
pnp->pn_path = pnp->pn_buf;
} else {
if ((pnp->pn_path - pnp->pn_buf) - complen +
pnp->pn_pathlen + sympnp->pn_pathlen >= pnp->pn_bufsize)
return (ENAMETOOLONG);
if (pnp->pn_pathlen != 0)
ovbcopy(pnp->pn_path, pnp->pn_path - complen +
sympnp->pn_pathlen, pnp->pn_pathlen + 1);
pnp->pn_path -= complen;
bcopy(sympnp->pn_path, pnp->pn_path, sympnp->pn_pathlen);
pnp->pn_pathlen += sympnp->pn_pathlen;
}
return (0);
}
int
pn_getsymlink(vnode_t *vp, struct pathname *pnp, cred_t *crp)
{
struct iovec aiov;
struct uio auio;
int error;
aiov.iov_base = pnp->pn_path = pnp->pn_buf;
aiov.iov_len = pnp->pn_bufsize;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_loffset = 0;
auio.uio_segflg = UIO_SYSSPACE;
auio.uio_extflg = UIO_COPY_CACHED;
auio.uio_resid = pnp->pn_bufsize;
if ((error = VOP_READLINK(vp, &auio, crp, NULL)) == 0) {
pnp->pn_pathlen = pnp->pn_bufsize - auio.uio_resid;
if (pnp->pn_pathlen == pnp->pn_bufsize)
error = ENAMETOOLONG;
else
pnp->pn_path[pnp->pn_pathlen] = '\0';
}
return (error);
}
int
pn_getcomponent(struct pathname *pnp, char *component)
{
char c, *cp, *path, saved;
size_t pathlen;
path = pnp->pn_path;
pathlen = pnp->pn_pathlen;
if (pathlen >= MAXNAMELEN) {
saved = path[MAXNAMELEN];
path[MAXNAMELEN] = '/';
for (cp = path; (c = *cp) != '/'; cp++)
*component++ = c;
path[MAXNAMELEN] = saved;
if (cp - path == MAXNAMELEN)
return (ENAMETOOLONG);
} else {
path[pathlen] = '/';
for (cp = path; (c = *cp) != '/'; cp++)
*component++ = c;
path[pathlen] = '\0';
}
pnp->pn_path = cp;
pnp->pn_pathlen = pathlen - (cp - path);
*component = '\0';
return (0);
}
void
pn_skipslash(struct pathname *pnp)
{
while (pnp->pn_pathlen > 0 && *pnp->pn_path == '/') {
pnp->pn_path++;
pnp->pn_pathlen--;
}
}
void
pn_setlast(struct pathname *pnp)
{
char *buf = pnp->pn_buf;
char *path = pnp->pn_path + pnp->pn_pathlen - 1;
char *endpath;
while (path > buf && *path == '/')
--path;
endpath = path + 1;
while (path > buf && *path != '/')
--path;
if (*path == '/')
path++;
*endpath = '\0';
pnp->pn_path = path;
pnp->pn_pathlen = endpath - path;
}
int
pn_fixslash(struct pathname *pnp)
{
char *start = pnp->pn_path;
char *end = start + pnp->pn_pathlen;
while (end > start && *(end - 1) == '/')
end--;
if (pnp->pn_pathlen == end - start)
return (0);
*end = '\0';
pnp->pn_pathlen = end - start;
return (1);
}
int
pn_addslash(struct pathname *pnp)
{
if (pnp->pn_path + pnp->pn_pathlen + 1 >=
pnp->pn_buf + pnp->pn_bufsize) {
if (pnp->pn_pathlen + 1 >= pnp->pn_bufsize)
return (ENAMETOOLONG);
ovbcopy(pnp->pn_path, pnp->pn_buf, pnp->pn_pathlen);
pnp->pn_path = pnp->pn_buf;
}
pnp->pn_path[pnp->pn_pathlen++] = '/';
pnp->pn_path[pnp->pn_pathlen] = '\0';
return (0);
}