#include <sys/systm.h>
#include <sys/inttypes.h>
#include <sys/cred.h>
#include <sys/vnode.h>
#include <sys/vfs.h>
#include <sys/filio.h>
#include <sys/uio.h>
#include <sys/dirent.h>
#include <sys/errno.h>
#include <sys/sysmacros.h>
#include <sys/kmem.h>
#include <sys/stat.h>
#include <sys/cmn_err.h>
#include <sys/u8_textprep.h>
#include <netsmb/smb_osdep.h>
#include <netsmb/smb.h>
#include <netsmb/smb2.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_subr.h>
#include <netsmb/smb_rq.h>
#include <smbfs/smbfs.h>
#include <smbfs/smbfs_node.h>
#include <smbfs/smbfs_subr.h>
#include <fs/fs_subr.h>
int
smbfs_get_xattrdir(vnode_t *pvp, vnode_t **vpp, cred_t *cr, int flags)
{
vnode_t *xvp;
smbnode_t *pnp, *xnp;
pnp = VTOSMB(pvp);
if (pnp->n_flag & N_XATTR)
return (EINVAL);
xnp = smbfs_node_findcreate(pnp->n_mount,
pnp->n_rpath, pnp->n_rplen, NULL, 0, ':',
&smbfs_fattr0);
ASSERT(xnp != NULL);
xvp = SMBTOV(xnp);
if (xvp->v_type == VNON) {
mutex_enter(&xvp->v_lock);
xvp->v_type = VDIR;
xvp->v_flag |= V_XATTRDIR;
mutex_exit(&xvp->v_lock);
mutex_enter(&xnp->r_statelock);
xnp->n_flag |= N_XATTR;
mutex_exit(&xnp->r_statelock);
}
*vpp = xvp;
return (0);
}
int
smbfs_xa_parent(vnode_t *vp, vnode_t **vpp)
{
smbnode_t *np = VTOSMB(vp);
smbnode_t *pnp;
int rplen;
*vpp = NULL;
if ((np->n_flag & N_XATTR) == 0)
return (EINVAL);
if (vp->v_flag & V_XATTRDIR) {
rplen = np->n_rplen - 1;
if (rplen < 1) {
SMBVDEBUG("rplen < 1?");
return (ENOENT);
}
if (np->n_rpath[rplen] != ':') {
SMBVDEBUG("last is not colon");
return (ENOENT);
}
} else {
for (rplen = 1; rplen < np->n_rplen; rplen++)
if (np->n_rpath[rplen] == ':')
break;
if (rplen >= np->n_rplen) {
SMBVDEBUG("colon not found");
return (ENOENT);
}
rplen++;
if (rplen >= np->n_rplen) {
SMBVDEBUG("no stream name");
return (ENOENT);
}
}
pnp = smbfs_node_findcreate(np->n_mount,
np->n_rpath, rplen, NULL, 0, 0,
&smbfs_fattr0);
ASSERT(pnp != NULL);
*vpp = SMBTOV(pnp);
return (0);
}
int
smbfs_xa_exists(vnode_t *vp, cred_t *cr)
{
smbnode_t *xnp;
vnode_t *xvp;
struct smb_cred scred;
struct smbfs_fctx ctx;
int error, rc = 0;
error = smbfs_get_xattrdir(vp, &xvp, cr, LOOKUP_XATTR);
if (error)
return (0);
xnp = VTOSMB(xvp);
smb_credinit(&scred, cr);
bzero(&ctx, sizeof (ctx));
ctx.f_flags = SMBFS_RDD_FINDFIRST;
ctx.f_dnp = xnp;
ctx.f_scred = &scred;
ctx.f_ssp = xnp->n_mount->smi_share;
error = smbfs_xa_findopen(&ctx, xnp, "*", 1);
if (error)
goto out;
error = smbfs_xa_findnext(&ctx, 1);
if (error)
goto out;
SMBVDEBUG("ctx.f_name: %s\n", ctx.f_name);
rc = 1;
out:
(void) smbfs_xa_findclose(&ctx);
smb_credrele(&scred);
VN_RELE(xvp);
return (rc);
}
int
smbfs_xa_getfattr(struct smbnode *xnp, struct smbfattr *fap,
struct smb_cred *scrp)
{
vnode_t *xvp;
vnode_t *pvp;
smbnode_t *pnp;
int error, nlen;
const char *name, *sname;
xvp = SMBTOV(xnp);
if (xvp->v_flag & V_XATTRDIR) {
fap->fa_attr = SMB_FA_DIR;
fap->fa_size = DEV_BSIZE;
return (0);
}
error = smbfs_xa_parent(xvp, &pvp);
if (error)
return (error);
pnp = VTOSMB(pvp);
ASSERT(xnp->n_rplen > pnp->n_rplen);
nlen = xnp->n_rplen - pnp->n_rplen;
name = xnp->n_rpath + pnp->n_rplen;
sname = name;
error = smbfs_smb_lookup(pnp, &name, &nlen, fap, scrp);
if (error == 0 && name != sname)
smbfs_name_free(name, nlen);
VN_RELE(pvp);
return (error);
}
static int
smbfs_xa_get_streaminfo(struct smbfs_fctx *ctx)
{
vnode_t *pvp;
smbnode_t *pnp;
smbnode_t *dnp = ctx->f_dnp;
struct mdchain *mdp;
int error;
error = smbfs_xa_parent(SMBTOV(dnp), &pvp);
if (error)
return (error);
ASSERT(pvp);
pnp = VTOSMB(pvp);
mdp = &ctx->f_mdchain;
md_done(mdp);
if (SSTOVC(ctx->f_ssp)->vc_flags & SMBV_SMB2) {
error = smbfs_smb2_get_streaminfo(pnp, mdp, ctx->f_scred);
} else {
error = smbfs_smb1_get_streaminfo(pnp, mdp, ctx->f_scred);
}
if (error)
goto out;
ctx->f_left = m_fixhdr(mdp->md_top);
ctx->f_eofs = 0;
ctx->f_flags |= SMBFS_RDD_EOF;
out:
VN_RELE(pvp);
return (error);
}
int
smbfs_xa_findopen(struct smbfs_fctx *ctx, struct smbnode *dnp,
const char *wildcard, int wclen)
{
ASSERT(dnp->n_flag & N_XATTR);
ctx->f_type = ft_XA;
ctx->f_namesz = SMB_MAXFNAMELEN + 1;
ctx->f_name = kmem_alloc(ctx->f_namesz, KM_SLEEP);
ctx->f_infolevel = FileStreamInformation;
ctx->f_wildcard = wildcard;
ctx->f_wclen = wclen;
return (0);
}
int
smbfs_xa_findnext(struct smbfs_fctx *ctx, uint16_t limit)
{
int error;
again:
if ((ctx->f_eofs + 8) > ctx->f_left) {
if (ctx->f_flags & SMBFS_RDD_EOF)
return (ENOENT);
ctx->f_limit = limit;
error = smbfs_xa_get_streaminfo(ctx);
if (error)
return (error);
ctx->f_otws++;
}
error = smbfs_decode_dirent(ctx);
if (error)
return (error);
SMBVDEBUG("name: %s\n", ctx->f_name);
if (ctx->f_nmlen >= 6) {
char *p = ctx->f_name + ctx->f_nmlen - 6;
if (strncmp(p, ":$DATA", 6) == 0) {
*p = '\0';
ctx->f_nmlen -= 6;
}
}
if (ctx->f_nmlen == 0)
goto again;
if (ctx->f_flags & SMBFS_RDD_FINDSINGLE) {
if (ctx->f_wclen != ctx->f_nmlen)
goto again;
if (u8_strcmp(ctx->f_wildcard, ctx->f_name,
ctx->f_nmlen, U8_STRCMP_CI_LOWER,
U8_UNICODE_LATEST, &error) || error)
goto again;
}
return (0);
}
int
smbfs_xa_findclose(struct smbfs_fctx *ctx)
{
if (ctx->f_name)
kmem_free(ctx->f_name, ctx->f_namesz);
return (0);
}