summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgwr <none@none>2008-07-04 06:02:33 -0700
committergwr <none@none>2008-07-04 06:02:33 -0700
commit91d632c867159b669d90fc7e172295433d0519ef (patch)
tree97bf28def94f6c33896c503fb582e7030e769548
parentb9499e44f6b72c94d917f5fbef046ddd085a973c (diff)
downloadillumos-joyent-91d632c867159b669d90fc7e172295433d0519ef.tar.gz
6668593 Support NT "named streams"
6720550 rename over existing file should attempt to remove
-rw-r--r--usr/src/uts/common/Makefile.files8
-rw-r--r--usr/src/uts/common/fs/smbclnt/netsmb/smb_subrs.c2
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.c29
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h4
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c344
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c26
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h24
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c28
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c78
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c377
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_xattr.c491
11 files changed, 1077 insertions, 334 deletions
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files
index e8791cbbb7..20337b2975 100644
--- a/usr/src/uts/common/Makefile.files
+++ b/usr/src/uts/common/Makefile.files
@@ -1047,10 +1047,10 @@ NSMB_OBJS += smb_conn.o smb_crypt.o smb_dev.o smb_iod.o \
smb_rq.o smb_smb.o smb_tran.o smb_trantcp.o \
smb_usr.o smb_subrs.o subr_mchain.o smb_pass.o
-SMBFS_OBJS += smbfs_vfsops.o smbfs_vnops.o smbfs_node.o \
- smbfs_acl.o smbfs_client.o smbfs_io.o \
- smbfs_smb.o smbfs_subr.o smbfs_subr2.o \
- smbfs_rwlock.o
+SMBFS_OBJS += smbfs_vfsops.o smbfs_vnops.o smbfs_node.o \
+ smbfs_acl.o smbfs_client.o smbfs_io.o \
+ smbfs_smb.o smbfs_subr.o smbfs_subr2.o \
+ smbfs_rwlock.o smbfs_xattr.o
#
diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_subrs.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_subrs.c
index 41341a0308..79eba6d6ba 100644
--- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_subrs.c
+++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_subrs.c
@@ -373,6 +373,7 @@ static struct {
{NT_STATUS_NO_SUCH_FILE, ENOENT},
{NT_STATUS_OBJECT_NAME_COLLISION, EEXIST},
{NT_STATUS_OBJECT_NAME_NOT_FOUND, ENOENT},
+ {NT_STATUS_OBJECT_NAME_INVALID, EINVAL},
{NT_STATUS_OBJECT_PATH_INVALID, ENOTDIR},
{NT_STATUS_PAGEFILE_QUOTA, EDQUOT},
{NT_STATUS_PASSWORD_EXPIRED, EACCES},
@@ -978,6 +979,7 @@ smb_maperror(int eclass, int eno)
case ERRbadformat:
case ERRremcd:
case ERRrmuns:
+ case ERRunknownlevel:
return (EINVAL);
case ERRbadfile:
case ERRbadpath:
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.c
index 6a2b184f47..594dedecc0 100644
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.c
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.c
@@ -224,7 +224,9 @@ smbfs_nget(vnode_t *dvp, const char *name, int nmlen,
struct smbfattr *fap, vnode_t **vpp)
{
struct smbnode *dnp = VTOSMB(dvp);
+ struct smbnode *np;
vnode_t *vp;
+ char sep;
*vpp = NULL;
@@ -235,10 +237,16 @@ smbfs_nget(vnode_t *dvp, const char *name, int nmlen,
return (EINVAL);
}
- /* The real work is in this call... */
+ /*
+ * See the comment near the top of smbfs_xattr.c about
+ * the logic for what separators to use where.
+ */
+ sep = (dnp->n_flag & N_XATTR) ? 0 : '\\';
+
+ /* Find or create the node. */
vp = smbfs_make_node(dvp->v_vfsp,
dnp->n_rpath, dnp->n_rplen,
- name, nmlen, fap);
+ name, nmlen, sep, fap);
/*
* We always have a vp now, because
@@ -246,6 +254,16 @@ smbfs_nget(vnode_t *dvp, const char *name, int nmlen,
* calls kmem_alloc with KM_SLEEP.
*/
ASSERT(vp);
+ np = VTOSMB(vp);
+
+ /*
+ * Files in an XATTR dir are also XATTR.
+ */
+ if (dnp->n_flag & N_XATTR) {
+ mutex_enter(&np->r_statelock);
+ np->n_flag |= N_XATTR;
+ mutex_exit(&np->r_statelock);
+ }
#ifdef NOT_YET
/* update the attr_cache info if the file is clean */
@@ -287,7 +305,12 @@ smbfs_attr_cacheenter(vnode_t *vp, struct smbfattr *fap)
mutex_enter(&np->r_statelock);
- vtype = vp->v_type;
+ vtype = (fap->fa_attr & SMB_FA_DIR) ? VDIR : VREG;
+ if (vp->v_type != vtype)
+ SMBVDEBUG("vtype change %d to %d\n",
+ vp->v_type, vtype);
+ vp->v_type = vtype;
+
if (vtype == VREG) {
if (np->n_size != fap->fa_size) {
/*
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h
index e05a0b4a5d..98adac6c21 100644
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h
@@ -175,6 +175,7 @@ struct mntinfo; /* defined in smbfs/smbfs_clnt.h */
#define NATTRCHANGED 0x02000 /* use smbfs_attr_cacheremove at close */
#define NALLOC 0x04000 /* being created */
#define NWALLOC 0x08000 /* awaiting creation */
+#define N_XATTR 0x10000 /* extended attribute (dir or file) */
typedef struct smbnode {
/* from Sun NFS struct rnode (XXX: cleanup needed) */
@@ -218,7 +219,8 @@ typedef struct smbnode {
uint32_t n_flag;
smbmntinfo_t *n_mount;
ino64_t n_ino;
- /* Lock for the next 7 is r_lkserlock */
+ /* Lock for the next 8 is r_lkserlock */
+ enum vtype n_ovtype; /* vnode type opened */
int n_dirrefs;
struct smbfs_fctx *n_dirseq; /* ff context */
long n_dirofs; /* last ff offset */
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c
index 2538937d2a..1a7a22bbe1 100644
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c
@@ -200,6 +200,14 @@ smbfs_smb_getfattr(
*/
ASSERT(np->r_lkserlock.count != 0);
+ /*
+ * Extended attribute directory or file.
+ */
+ if (np->n_flag & N_XATTR) {
+ error = smbfs_xa_getfattr(np, fap, scrp);
+ return (error);
+ }
+
if (np->n_fidrefs)
error = smbfs_smb_qfileinfo(np, fap, scrp, 0);
else
@@ -210,10 +218,13 @@ smbfs_smb_getfattr(
error = smbfs_smb_query_info(np, NULL, 0, fap, scrp);
}
-#if 0 /* Moved this part to caller. */
- if (!error && fap->fa_mtime.tv_sec == 0)
- smbfs_attr_touchdir(dnp);
-#endif
+ /*
+ * Note directory size is not provided by
+ * windows servers (they leave it as zero)
+ */
+ if ((fap->fa_attr & SMB_FA_DIR) &&
+ (fap->fa_size < DEV_BSIZE))
+ fap->fa_size = DEV_BSIZE;
return (error);
}
@@ -488,198 +499,8 @@ top:
/*
* Support functions for _qstreaminfo
- * Todo: show NT file streams as
- * Solaris named attributes.
+ * See smbfs_xattr.c
*/
-#ifdef APPLE
-
-static char *
-sfm2xattr(char *sfm)
-{
- if (!strncasecmp(sfm, SFM_RESOURCEFORK_NAME,
- sizeof (SFM_RESOURCEFORK_NAME)))
- return (XATTR_RESOURCEFORK_NAME);
- if (!strncasecmp(sfm, SFM_FINDERINFO_NAME,
- sizeof (SFM_FINDERINFO_NAME)))
- return (XATTR_FINDERINFO_NAME);
- return (NULL);
-}
-
-static int
-smbfs_smb_undollardata(struct smbnode *np, struct smbfs_fctx *ctx)
-{
- char *cp;
- int len = strlen(SMB_DATASTREAM);
-
- if (!ctx->f_name) /* sanity check */
- goto bad;
- if (ctx->f_nmlen < len + 1) /* "::$DATA" at a minimum */
- goto bad;
- if (*ctx->f_name != ':') /* leading colon - "always" */
- goto bad;
- cp = &ctx->f_name[ctx->f_nmlen - len]; /* point to 2nd colon */
- if (bcmp(cp, SMB_DATASTREAM, len))
- goto bad;
- if (ctx->f_nmlen == len + 1) /* merely the data fork? */
- return (0); /* skip it */
- /*
- * XXX here we should be calling KPI to validate the stream name
- */
- if (ctx->f_nmlen >= 18 &&
- !(bcmp(ctx->f_name, ":com.apple.system.", 18) == 0))
- return (0); /* skip protected system attrs */
- if (ctx->f_nmlen - len > XATTR_MAXNAMELEN + 1)
- goto bad; /* mustnt return more than 128 bytes */
- /*
- * Un-count a colon and the $DATA, then the
- * 2nd colon is replaced by a terminating null.
- */
- ctx->f_nmlen -= len;
- *cp = '\0';
- return (1);
-bad:
- SMBSDEBUG("file \"%.*s\" has bad stream \"%.*s\"\n",
- np->n_nmlen, np->n_name, ctx->f_nmlen, ctx->f_name);
- return (0); /* skip it */
-}
-
-PRIVSYM int
-smbfs_smb_qstreaminfo(struct smbnode *np, struct smb_cred *scrp,
- uio_t uio, size_t *sizep)
-{
- struct smb_share *ssp = np->n_mount->smi_share;
- struct smb_vc *vcp = SSTOVC(ssp);
- struct smb_t2rq *t2p;
- int error;
- struct mbchain *mbp;
- struct mdchain *mdp;
- uint32_t next, nlen, used;
- struct smbfs_fctx ctx;
-
- *sizep = 0;
- ctx.f_ssp = ssp;
- ctx.f_name = NULL;
-
- error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_QUERY_PATH_INFORMATION,
- scrp, &t2p);
- if (error)
- return (error);
- mbp = &t2p->t2_tparam;
- mb_init(mbp);
- /*
- * SMB_QFILEINFO_STREAM_INFORMATION is an option to consider
- * here. Samba declined to support the older info level with
- * a comment claiming doing so caused a BSOD.
- */
- mb_put_uint16le(mbp, SMB_QFILEINFO_STREAM_INFO);
- mb_put_uint32le(mbp, 0);
- /* mb_put_uint8(mbp, SMB_DT_ASCII); specs are wrong */
- error = smbfs_fullpath(mbp, vcp, np, NULL, NULL, '\\');
- if (error)
- goto out;
- t2p->t2_maxpcount = 2;
- t2p->t2_maxdcount = vcp->vc_txmax;
- error = smb_t2_request(t2p);
- if (error) {
- if (smb_t2_err(t2p) == NT_STATUS_INVALID_PARAMETER)
- error = ENOTSUP;
- goto out;
- }
- mdp = &t2p->t2_rdata;
- /*
- * On a directory Windows is likely to return a zero data count.
- * Check for that now to avoid EBADRPC from md_get_uint32le
- */
- if (mdp->md_cur == NULL)
- goto out;
- do {
- if ((error = md_get_uint32le(mdp, &next)))
- goto out;
- if ((error = md_get_uint32le(mdp, &nlen))) /* name length */
- goto out;
- if ((error = md_get_uint64le(mdp, NULL))) /* stream size */
- goto out;
- if ((error = md_get_uint64le(mdp, NULL))) /* allocated size */
- goto out;
- /*
- * Sanity check to limit DoS or buffer overrun attempts.
- * The arbitrary 16384 is sufficient for all legit packets.
- */
- if (nlen > 16384) {
- SMBVDEBUG("huge name length in packet!\n");
- error = EBADRPC;
- goto out;
- }
- ctx.f_name = kmem_zalloc(nlen, KM_SLEEP);
- ctx.f_namesz = nlen;
- if ((error = md_get_mem(mdp, ctx.f_name, nlen, MB_MSYSTEM)))
- goto out;
- /*
- * skip pad bytes and/or tail of overlong name
- */
- used = 4 + 4 + 8 + 8 + nlen;
- if (next && next > used) {
- if (next - used > 16384) {
- SMBVDEBUG("huge offset in packet!\n");
- error = EBADRPC;
- goto out;
- }
- md_get_mem(mdp, NULL, next - used, MB_MSYSTEM);
- }
- /* ignore a trailing null, not that we expect them */
- if (SMB_UNICODE_STRINGS(vcp)) {
- if (nlen > 1 && !ctx.f_name[nlen - 1] &&
- !ctx.f_name[nlen - 2])
- nlen -= 2;
- } else {
- if (nlen && !ctx.f_name[nlen - 1])
- nlen -= 1;
- }
- ctx.f_nmlen = nlen;
- smbfs_fname_tolocal(&ctx); /* converts from UCS2LE */
- /*
- * We should now have a name in the form
- * : <foo> :$DATA
- * Where <foo> is UTF-8 w/o null termination
- * If it isn't in that form we want to LOG it and skip it.
- * Note we want to skip w/o logging the "data fork" entry,
- * which is simply ::$DATA
- * Otherwise we want to uiomove out <foo> with a null added.
- */
- if (smbfs_smb_undollardata(np, &ctx)) {
- char *s;
-
- /* the "+ 1" skips over the leading colon */
- s = sfm2xattr(ctx.f_name + 1);
-#ifndef DUAL_EAS /* XXX */
- /*
- * In Tiger Carbon still accesses dot-underscore files directly, so...
- * For Tiger we preserve the SFM/Thursby AFP_* stream names rather
- * than mapping them to com.apple.*. This means our copy engines
- * will preserve SFM/Thursby resource-fork and finder-info.
- */
- s = NULL;
-#endif
- if (s)
- ctx.f_nmlen = strlen(s) + 1;
- else
- s = ctx.f_name + 1;
- if (uio)
- uiomove(s, ctx.f_nmlen, uio);
- else
- *sizep += ctx.f_nmlen;
- }
- kmem_free(ctx.f_name, ctx.f_namesz);
- ctx.f_name = NULL;
- } while (next && !error);
-out:
- if (ctx.f_name)
- kmem_free(ctx.f_name, ctx.f_namesz);
- smb_t2_done(t2p);
- return (error);
-}
-
-#endif /* APPLE */
int
smbfs_smb_qfsattr(struct smb_share *ssp, uint32_t *attrp,
@@ -2150,6 +1971,7 @@ smbfs_smb_rename(struct smbnode *src, struct smbnode *tdnp,
struct mbchain *mbp;
int error;
uint16_t fa;
+ char sep;
error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_RENAME, scrp);
if (error)
@@ -2162,20 +1984,28 @@ smbfs_smb_rename(struct smbnode *src, struct smbnode *tdnp,
mb_put_uint16le(mbp, fa);
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
+
+ /*
+ * When we're not adding any component name, the
+ * passed sep is ignored, so just pass sep=0.
+ */
mb_put_uint8(mbp, SMB_DT_ASCII);
- do {
- error = smbfs_fullpath(mbp, SSTOVC(ssp), src, NULL, NULL, '\\');
- if (error)
- break;
- mb_put_uint8(mbp, SMB_DT_ASCII);
- error = smbfs_fullpath(mbp, SSTOVC(ssp), tdnp, tname, &tnmlen,
- '\\');
- if (error)
- break;
- smb_rq_bend(rqp);
- error = smb_rq_simple(rqp);
- /*LINTED*/
- } while (0);
+ error = smbfs_fullpath(mbp, SSTOVC(ssp), src, NULL, NULL, 0);
+ if (error)
+ goto out;
+
+ /*
+ * After XATTR directories, separator is ":"
+ */
+ sep = (src->n_flag & N_XATTR) ? ':' : '\\';
+ mb_put_uint8(mbp, SMB_DT_ASCII);
+ error = smbfs_fullpath(mbp, SSTOVC(ssp), tdnp, tname, &tnmlen, sep);
+ if (error)
+ goto out;
+
+ smb_rq_bend(rqp);
+ error = smb_rq_simple(rqp);
+out:
smb_rq_done(rqp);
return (error);
}
@@ -2376,9 +2206,10 @@ smbfs_smb_search(struct smbfs_fctx *ctx)
/*ARGSUSED*/
static int
smbfs_smb_findopenLM1(struct smbfs_fctx *ctx, struct smbnode *dnp,
- const char *wildcard, int wclen, uint16_t attr, struct smb_cred *scrp)
+ const char *wildcard, int wclen, uint16_t attr)
{
- /* #pragma unused(dnp, scrp) */
+
+ ctx->f_type = ft_LM1;
ctx->f_attrmask = attr;
if (wildcard) {
if (wclen == 1 && wildcard[0] == '*') {
@@ -2607,8 +2438,10 @@ smbfs_smb_findclose2(struct smbfs_fctx *ctx)
/*ARGSUSED*/
static int
smbfs_smb_findopenLM2(struct smbfs_fctx *ctx, struct smbnode *dnp,
- const char *wildcard, int wclen, uint16_t attr, struct smb_cred *scrp)
+ const char *wildcard, int wclen, uint16_t attr)
{
+
+ ctx->f_type = ft_LM2;
ctx->f_namesz = SMB_MAXFNAMELEN;
if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp)))
ctx->f_namesz *= 2;
@@ -2808,6 +2641,8 @@ again:
ctx->f_eofs = next;
ctx->f_ecnt--;
ctx->f_left--;
+
+ smbfs_fname_tolocal(ctx);
return (0);
}
@@ -2825,7 +2660,7 @@ smbfs_smb_findcloseLM2(struct smbfs_fctx *ctx)
}
int
-smbfs_smb_findopen(struct smbnode *dnp, const char *wildcard, int wclen,
+smbfs_smb_findopen(struct smbnode *dnp, const char *wild, int wlen,
int attr, struct smb_cred *scrp,
struct smbfs_fctx **ctxpp)
{
@@ -2836,20 +2671,25 @@ smbfs_smb_findopen(struct smbnode *dnp, const char *wildcard, int wclen,
if (ctx == NULL)
return (ENOMEM);
bzero(ctx, sizeof (*ctx));
- if (dnp->n_mount->smi_share) {
- ctx->f_ssp = dnp->n_mount->smi_share;
- }
- ctx->f_dnp = dnp;
+
ctx->f_flags = SMBFS_RDD_FINDFIRST;
+ ctx->f_dnp = dnp;
ctx->f_scred = scrp;
+ ctx->f_ssp = dnp->n_mount->smi_share;
+
+ if (dnp->n_flag & N_XATTR) {
+ error = smbfs_xa_findopen(ctx, dnp, wild, wlen);
+ goto out;
+ }
+
if (SMB_DIALECT(SSTOVC(ctx->f_ssp)) < SMB_DIALECT_LANMAN2_0 ||
(dnp->n_mount->smi_args.flags & SMBFS_MOUNT_NO_LONG)) {
- ctx->f_flags |= SMBFS_RDD_USESEARCH;
- error = smbfs_smb_findopenLM1(ctx, dnp, wildcard, wclen,
- attr, scrp);
- } else
- error = smbfs_smb_findopenLM2(ctx, dnp, wildcard, wclen,
- attr, scrp);
+ error = smbfs_smb_findopenLM1(ctx, dnp, wild, wlen, attr);
+ } else {
+ error = smbfs_smb_findopenLM2(ctx, dnp, wild, wlen, attr);
+ }
+
+out:
if (error)
(void) smbfs_smb_findclose(ctx, scrp);
else
@@ -2883,30 +2723,40 @@ smbfs_smb_findnext(struct smbfs_fctx *ctx, int limit, struct smb_cred *scrp)
ctx->f_scred = scrp;
for (;;) {
- if (ctx->f_flags & SMBFS_RDD_USESEARCH) {
+ bzero(&ctx->f_attr, sizeof (ctx->f_attr));
+ switch (ctx->f_type) {
+ case ft_LM1:
error = smbfs_smb_findnextLM1(ctx, (uint16_t)limit);
- } else
+ break;
+ case ft_LM2:
error = smbfs_smb_findnextLM2(ctx, (uint16_t)limit);
+ break;
+ case ft_XA:
+ error = smbfs_xa_findnext(ctx, (uint16_t)limit);
+ break;
+ default:
+ ASSERT(0);
+ error = EINVAL;
+ break;
+ }
if (error)
return (error);
- if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp))) {
- /*LINTED*/
- uint16_t *up = (uint16_t *)ctx->f_name;
-
- /* Do comparisons on UCS-2LE characters */
- if ((ctx->f_nmlen == 2 && up[0] == htoles('.')) ||
- (ctx->f_nmlen == 4 && up[0] == htoles('.') &&
- up[1] == htoles('.')))
- continue;
- } else {
- if ((ctx->f_nmlen == 1 && ctx->f_name[0] == '.') ||
- (ctx->f_nmlen == 2 && ctx->f_name[0] == '.' &&
- ctx->f_name[1] == '.'))
- continue;
- }
+ /*
+ * Skip "." or ".." - easy now that ctx->f_name
+ * has already been converted to utf-8 format.
+ */
+ if ((ctx->f_nmlen == 1 && ctx->f_name[0] == '.') ||
+ (ctx->f_nmlen == 2 && ctx->f_name[0] == '.' &&
+ ctx->f_name[1] == '.'))
+ continue;
break;
}
- smbfs_fname_tolocal(ctx);
+
+ /*
+ * Moved the smbfs_fname_tolocal(ctx) call into
+ * the ..._findnext functions above.
+ */
+
ctx->f_attr.fa_ino = smbfs_getino(ctx->f_dnp, ctx->f_name,
ctx->f_nmlen);
return (0);
@@ -2917,11 +2767,19 @@ int
smbfs_smb_findclose(struct smbfs_fctx *ctx, struct smb_cred *scrp)
{
int error;
+
ctx->f_scred = scrp;
- if (ctx->f_flags & SMBFS_RDD_USESEARCH) {
+ switch (ctx->f_type) {
+ case ft_LM1:
error = smbfs_smb_findcloseLM1(ctx);
- } else
+ break;
+ case ft_LM2:
error = smbfs_smb_findcloseLM2(ctx);
+ break;
+ case ft_XA:
+ error = smbfs_xa_findclose(ctx);
+ break;
+ }
if (ctx->f_rname)
kmem_free(ctx->f_rname, ctx->f_rnamelen);
if (ctx->f_firstnm)
@@ -2967,8 +2825,6 @@ smbfs_smb_lookup(struct smbnode *dnp, const char **namep, int *nmlenp,
if (smbfs_rw_enter_sig(&dnp->r_lkserlock, RW_READER, intr))
return (EINTR);
- bzero(fap, sizeof (*fap));
-
/*
* This hides a server bug observable in Win98:
* size changes may not show until a CLOSE or a FLUSH op
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c
index 5ef9b5439f..eaf3fb2238 100644
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c
@@ -289,12 +289,34 @@ smbfs_fullpath(struct mbchain *mbp, struct smb_vc *vcp, struct smbnode *dnp,
if (error)
return (error);
}
+
error = smb_put_dmem(mbp, vcp,
dnp->n_rpath, dnp->n_rplen,
caseopt, lenp);
if (name) {
- /* If not at root, put separator */
- if (dnp->n_rplen > 1) {
+ /*
+ * Special case at share root:
+ * Don't put another slash.
+ */
+ if (dnp->n_rplen <= 1 && sep == '\\')
+ sep = 0;
+ /*
+ * More special cases, now for XATTR:
+ * Our "faked up" XATTR directories use a
+ * full path name ending with ":" so as to
+ * avoid conflicts with any real paths.
+ * (It is not a valid CIFS path name.)
+ * Therefore, when we're composing a full
+ * path name from an XATTR directory, we
+ * need to _ommit_ the ":" separator and
+ * instead copy the one from the "fake"
+ * parent node's path name.
+ */
+ if (dnp->n_flag & N_XATTR)
+ sep = 0;
+
+ if (sep) {
+ /* Put the separator */
if (unicode)
error = mb_put_uint16le(mbp, sep);
else
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h
index feb186ac76..b2382d5438 100644
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h
@@ -78,6 +78,14 @@ struct smb_vc;
struct statvfs;
struct timespec;
+/*
+ * Types of find_first, find_next context objects
+ */
+typedef enum {
+ ft_LM1 = 1,
+ ft_LM2,
+ ft_XA
+} smbfs_fctx_type_t;
/*
* Context to perform findfirst/findnext/findclose operations
@@ -85,7 +93,7 @@ struct timespec;
#define SMBFS_RDD_FINDFIRST 0x01
#define SMBFS_RDD_EOF 0x02
#define SMBFS_RDD_FINDSINGLE 0x04
-#define SMBFS_RDD_USESEARCH 0x08
+/* note SMBFS_RDD_USESEARCH 0x08 replaced by smbfs_fctx_type */
#define SMBFS_RDD_NOCLOSE 0x10
#define SMBFS_RDD_GOTRNAME 0x1000
@@ -99,6 +107,7 @@ struct smbfs_fctx {
/*
* Setable values
*/
+ smbfs_fctx_type_t f_type;
int f_flags; /* SMBFS_RDD_ */
/*
* Return values
@@ -248,7 +257,7 @@ void smb_dos2unixtime(uint_t dd, uint_t dt, uint_t dh, int tzoff,
vnode_t *smbfs_make_node(vfs_t *vfsp,
const char *dir, int dirlen,
const char *name, int nmlen,
- struct smbfattr *fap);
+ char sep, struct smbfattr *fap);
void smb_addfree(smbnode_t *sp);
void smb_addhash(smbnode_t *sp);
void smb_rmhash(smbnode_t *);
@@ -265,6 +274,17 @@ int smbfs_writevnode(vnode_t *vp, uio_t *uiop, cred_t *cr,
int ioflag, int timo);
int smbfsgetattr(vnode_t *vp, struct vattr *vap, cred_t *cr);
+/* smbfs_xattr.c */
+int smbfs_get_xattrdir(vnode_t *dvp, vnode_t **vpp, cred_t *cr, int);
+int smbfs_xa_parent(vnode_t *vp, vnode_t **vpp);
+int smbfs_xa_exists(vnode_t *vp, cred_t *cr);
+int smbfs_xa_getfattr(struct smbnode *np, struct smbfattr *fap,
+ struct smb_cred *scrp);
+int smbfs_xa_findopen(struct smbfs_fctx *ctx, struct smbnode *dnp,
+ const char *name, int nmlen);
+int smbfs_xa_findnext(struct smbfs_fctx *ctx, uint16_t limit);
+int smbfs_xa_findclose(struct smbfs_fctx *ctx);
+
/* For Solaris, interruptible rwlock */
int smbfs_rw_enter_sig(smbfs_rwlock_t *l, krw_t rw, int intr);
int smbfs_rw_tryenter(smbfs_rwlock_t *l, krw_t rw);
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c
index c7ae9131a1..cb712c489a 100644
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c
@@ -174,6 +174,7 @@ smbfs_make_node(
int dirlen,
const char *name,
int nmlen,
+ char sep,
struct smbfattr *fap)
{
char *rpath;
@@ -189,26 +190,33 @@ smbfs_make_node(
/*
* Build the full path name in allocated memory
- * so we have it for lookup, etc.
+ * so we have it for lookup, etc. Note the
+ * special case at the root (dir=="\\", dirlen==1)
+ * where this does not add a slash separator.
+ * To do that would make a double slash, which
+ * has special meaning in CIFS.
*
* ToDo: Would prefer to allocate a remote path
* only when we will create a new node.
*/
+ if (dirlen <= 1 && sep == '\\')
+ sep = '\0'; /* no slash */
+
+ /* Compute the length of rpath and allocate. */
rplen = dirlen;
- if (name) {
- /* If not at root, we'll add a slash. */
- if (dirlen > 1)
- rplen++;
+ if (sep)
+ rplen++;
+ if (name)
rplen += nmlen;
- }
+
rpath = kmem_alloc(rplen + 1, KM_SLEEP);
+ /* Fill in rpath */
bcopy(dir, rpath, dirlen);
- if (name) {
- if (dirlen > 1)
- rpath[dirlen++] = '\\';
+ if (sep)
+ rpath[dirlen++] = sep;
+ if (name)
bcopy(name, &rpath[dirlen], nmlen);
- }
rpath[rplen] = 0;
hash = smbfs_hash(rpath, rplen);
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c
index f391dedd41..f86518902c 100644
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c
@@ -80,12 +80,51 @@ int smbfsinit(int fstyp, char *name);
void smbfsfini();
static int smbfs_mount_label_policy(vfs_t *, void *, int, cred_t *);
+/*
+ * SMBFS Mount options table for MS_OPTIONSTR
+ * Note: These are not all the options.
+ * Some options come in via MS_DATA.
+ * Others are generic (see vfs.c)
+ */
+static char *intr_cancel[] = { MNTOPT_NOINTR, NULL };
+static char *nointr_cancel[] = { MNTOPT_INTR, NULL };
+#ifdef NOT_YET
+static char *force_dio_cancel[] = { MNTOPT_NOFORCEDIRECTIO, NULL };
+static char *noforce_dio_cancel[] = { MNTOPT_FORCEDIRECTIO, NULL };
+static char *largefiles_cancel[] = { MNTOPT_NOLARGEFILES, NULL };
+static char *nolargefiles_cancel[] = { MNTOPT_LARGEFILES, NULL };
+#endif
+static char *xattr_cancel[] = { MNTOPT_NOXATTR, NULL };
+static char *noxattr_cancel[] = { MNTOPT_XATTR, NULL };
+
+static mntopt_t mntopts[] = {
+/*
+ * option name cancel option default arg flags
+ * ufs arg flag
+ */
+ { MNTOPT_INTR, intr_cancel, NULL, MO_DEFAULT, 0 },
+ { MNTOPT_NOINTR, nointr_cancel, NULL, 0, 0 },
+#ifdef NOT_YET
+ { MNTOPT_FORCEDIRECTIO, force_dio_cancel, NULL, 0, 0 },
+ { MNTOPT_NOFORCEDIRECTIO, noforce_dio_cancel, NULL, 0, 0 },
+ { MNTOPT_LARGEFILES, largefiles_cancel, NULL, MO_DEFAULT, 0 },
+ { MNTOPT_NOLARGEFILES, nolargefiles_cancel, NULL, 0, 0 },
+#endif
+ { MNTOPT_XATTR, xattr_cancel, NULL, MO_DEFAULT, 0 },
+ { MNTOPT_NOXATTR, noxattr_cancel, NULL, 0, 0 }
+};
+
+static mntopts_t smbfs_mntopts = {
+ sizeof (mntopts) / sizeof (mntopt_t),
+ mntopts
+};
+
static vfsdef_t vfw = {
VFSDEF_VERSION,
"smbfs", /* type name string */
smbfsinit, /* init routine */
- VSW_NOTZONESAFE, /* flags */
- NULL /* mount options table prototype */
+ VSW_HASPROTO|VSW_NOTZONESAFE, /* flags */
+ &smbfs_mntopts /* mount options table prototype */
};
static struct modlfs modlfs = {
@@ -297,6 +336,8 @@ smbfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
*
* uap->datalen might be different from sizeof (args)
* in a compatible situation.
+ *
+ * XXX - todo: handle mount options string
*/
STRUCT_INIT(args, get_udatamodel());
bzero(STRUCT_BUF(args), SIZEOF_STRUCT(smbfs_args, DATAMODEL_NATIVE));
@@ -419,6 +460,14 @@ proceed:
smi->smi_share = ssp;
ssp->ss_mount = smi;
smi->smi_zone = mntzone;
+ smi->smi_flags = SMI_LLOCK;
+
+ /*
+ * Handle mount options. See also XATTR below.
+ * XXX: forcedirectio, largefiles (later)
+ */
+ if (vfs_optionisset(vfsp, MNTOPT_INTR, NULL))
+ smi->smi_flags |= SMI_INT;
/*
* XXX If not root, get uid/gid from the covered vnode.
@@ -428,19 +477,23 @@ proceed:
smi->smi_args.uid = STRUCT_FGET(args, uid);
smi->smi_args.gid = STRUCT_FGET(args, gid);
+ /*
+ * Get attributes of the remote file system,
+ * i.e. ACL support, named streams, etc.
+ */
error = smbfs_smb_qfsattr(ssp, &smi->smi_fsattr, &scred);
if (error) {
SMBVDEBUG("smbfs_smb_qfsattr error %d\n", error);
}
-#ifdef NOT_YET
- /* Once acls are implemented, remove the ifdefs */
- else if (smbfs_aclsflunksniff(smi, &scred)) {
- mutex_enter(&smi->smi_lock);
- smi->smi_fsattr &= ~FILE_PERSISTENT_ACLS;
- mutex_exit(&smi->smi_lock);
- }
-#endif /* NOT_YET */
+ /*
+ * We enable XATTR by default (via smbfs_mntopts)
+ * but if the share does not support named streams,
+ * force the NOXATTR option (also clears XATTR).
+ * Caller will set or clear VFS_XATTR after this.
+ */
+ if ((smi->smi_fsattr & FILE_NAMED_STREAMS) == 0)
+ vfs_setmntopt(vfsp, MNTOPT_NOXATTR, NULL, 0);
/*
* Assign a unique device id to the mount
@@ -459,7 +512,6 @@ proceed:
vfsp->vfs_bsize = MAXBSIZE;
vfsp->vfs_bcount = 0;
- smi->smi_flags = SMI_INT | SMI_LLOCK;
smi->smi_vfsp = vfsp;
smbfs_zonelist_add(smi);
@@ -467,7 +519,7 @@ proceed:
* Create the root vnode, which we need in unmount
* for the call to smb_check_table(), etc.
*/
- rtvp = smbfs_make_node(vfsp, "\\", 1, NULL, 0, NULL);
+ rtvp = smbfs_make_node(vfsp, "\\", 1, NULL, 0, 0, NULL);
if (!rtvp) {
cmn_err(CE_WARN, "smbfs_mount: make_node failed\n");
return (ENOENT);
@@ -723,7 +775,7 @@ recheck:
stvfs.f_frsize = stvfs.f_bsize;
stvfs.f_favail = stvfs.f_ffree;
stvfs.f_fsid = (unsigned long)vfsp->vfs_fsid.val[0];
- strncpy(stvfs.f_basetype, vfw.name, FSTYPSZ);
+ (void) strncpy(stvfs.f_basetype, vfw.name, FSTYPSZ);
stvfs.f_flag = vf_to_stf(vfsp->vfs_flag);
stvfs.f_namemax = (uint32_t)MAXNAMELEN - 1;
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c
index 02cbe68382..7b3ebc2d7e 100644
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c
@@ -69,11 +69,14 @@
/*
* These characters are illegal in NTFS file names.
* ref: http://support.microsoft.com/kb/147438
+ *
+ * Careful! The check in the XATTR case skips the
+ * first character to allow colon in XATTR names.
*/
static const char illegal_chars[] = {
+ ':', /* colon - keep this first! */
'\\', /* back slash */
'/', /* slash */
- ':', /* colon */
'*', /* asterisk */
'?', /* question mark */
'"', /* double quote */
@@ -91,6 +94,10 @@ static const char illegal_chars[] = {
/* local static function defines */
+#ifdef USE_DNLC
+static int smbfslookup_dnlc(vnode_t *dvp, char *nm, vnode_t **vpp,
+ cred_t *cr);
+#endif
static int smbfslookup(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr,
int dnlc, caller_context_t *);
static int smbfsrename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm,
@@ -266,6 +273,23 @@ smbfs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
smb_credinit(&scred, curproc, cr);
/*
+ * Keep track of the vnode type at first open.
+ * It may change later, and we need close to do
+ * cleanup for the type we opened. Also deny
+ * open of new types until old type is closed.
+ * XXX: Per-open instance nodes whould help.
+ */
+ if (np->n_ovtype == VNON) {
+ ASSERT(np->n_dirrefs == 0);
+ ASSERT(np->n_fidrefs == 0);
+ } else if (np->n_ovtype != vp->v_type) {
+ SMBVDEBUG("open n_ovtype=%d v_type=%d\n",
+ np->n_ovtype, vp->v_type);
+ error = EACCES;
+ goto out;
+ }
+
+ /*
* Directory open is easy.
*/
if (vp->v_type == VDIR) {
@@ -311,10 +335,11 @@ smbfs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
/*
* we always ask for READ_CONTROL so we can always get the
- * owner/group IDs to satisfy a stat.
+ * owner/group IDs to satisfy a stat. Ditto attributes.
* XXX: verify that works with "drop boxes"
*/
- rights |= STD_RIGHT_READ_CONTROL_ACCESS;
+ rights |= (STD_RIGHT_READ_CONTROL_ACCESS |
+ SA_RIGHT_FILE_READ_ATTRIBUTES);
if ((flag & FREAD))
rights |= SA_RIGHT_FILE_READ_DATA;
if ((flag & FWRITE))
@@ -359,8 +384,14 @@ smbfs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
mutex_exit(&np->r_statelock);
have_fid:
- /* Get attributes (maybe). */
+ /*
+ * Keep track of the vnode type at first open.
+ * (see comments above)
+ */
+ if (np->n_ovtype == VNON)
+ np->n_ovtype = vp->v_type;
+ /* Get attributes (maybe). */
/* Darwin (derived) code. */
@@ -459,7 +490,15 @@ smbfs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
smb_credinit(&scred, curproc, cr);
error = 0;
- if (vp->v_type == VDIR) {
+
+ /*
+ * Note that vp->v_type may change if a remote node
+ * is deleted and recreated as a different type, and
+ * our getattr may change v_type accordingly.
+ * Now use n_ovtype to keep track of the v_type
+ * we had during open (see comments above).
+ */
+ if (np->n_ovtype == VDIR) {
struct smbfs_fctx *fctx;
ASSERT(np->n_dirrefs > 0);
if (--np->n_dirrefs)
@@ -484,6 +523,9 @@ smbfs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
error, np->n_rpath);
}
+ /* Allow next open to use any v_type. */
+ np->n_ovtype = VNON;
+
if (np->n_flag & NATTRCHANGED)
smbfs_attr_cacheremove(np);
@@ -744,6 +786,7 @@ smbfsgetattr(vnode_t *vp, struct vattr *vap, cred_t *cr)
return (EINTR);
smb_credinit(&scred, curproc, cr);
+ bzero(&fattr, sizeof (fattr));
error = smbfs_smb_getfattr(np, &fattr, &scred);
smb_credrele(&scred);
@@ -818,6 +861,19 @@ smbfssetattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr)
ASSERT(curproc->p_zone == smi->smi_zone);
/*
+ * There are no settable attributes on the XATTR dir,
+ * so just silently ignore these. On XATTR files,
+ * you can set the size but nothing else.
+ */
+ if (vp->v_flag & V_XATTRDIR)
+ return (0);
+ if (np->n_flag & N_XATTR) {
+ if (mask & AT_TIMES)
+ SMBVDEBUG("ignore set time on xattr\n");
+ mask &= AT_SIZE;
+ }
+
+ /*
* If our caller is trying to set multiple attributes, they
* can make no assumption about what order they are done in.
* Here we try to do them in order of decreasing likelihood
@@ -1181,19 +1237,45 @@ smbfs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp,
int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct,
int *direntflags, pathname_t *realpnp)
{
- int error;
- smbnode_t *dnp;
+ vfs_t *vfs;
smbmntinfo_t *smi;
+ smbnode_t *dnp;
+ int error;
- smi = VTOSMI(dvp);
+ vfs = dvp->v_vfsp;
+ smi = VFTOSMI(vfs);
if (curproc->p_zone != smi->smi_zone)
return (EPERM);
- if (smi->smi_flags & SMI_DEAD || dvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ if (smi->smi_flags & SMI_DEAD || vfs->vfs_flag & VFS_UNMOUNTED)
return (EIO);
dnp = VTOSMB(dvp);
+
+ /*
+ * Are we looking up extended attributes? If so, "dvp" is
+ * the file or directory for which we want attributes, and
+ * we need a lookup of the (faked up) attribute directory
+ * before we lookup the rest of the path.
+ */
+ if (flags & LOOKUP_XATTR) {
+ /*
+ * Require the xattr mount option.
+ */
+ if ((vfs->vfs_flag & VFS_XATTR) == 0)
+ return (EINVAL);
+
+ /*
+ * We don't allow recursive attributes.
+ */
+ if (dnp->n_flag & N_XATTR)
+ return (EINVAL);
+
+ error = smbfs_get_xattrdir(dvp, vpp, cr, flags);
+ return (error);
+ }
+
if (smbfs_rw_enter_sig(&dnp->r_rwlock, RW_READER, SMBINTR(dvp))) {
error = EINTR;
goto out;
@@ -1218,6 +1300,7 @@ smbfslookup(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr, int dnlc,
smbnode_t *dnp;
smbmntinfo_t *smi;
/* struct smb_vc *vcp; */
+ const char *ill;
const char *name = (const char *)nm;
int nmlen = strlen(nm);
int rplen;
@@ -1256,23 +1339,15 @@ smbfslookup(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr, int dnlc,
return (0);
}
- /* if the name is longer that what is supported, return an error */
- if (nmlen > supplen)
- return (ENAMETOOLONG);
-
/*
- * Avoid surprises with characters that are
- * illegal in Windows file names.
- * Todo: CATIA mappings XXX
+ * Can't do lookups in non-directories.
*/
- if (strpbrk(nm, illegal_chars))
- return (EINVAL);
-
- /* if the dvp is not a directory, return an error */
if (dvp->v_type != VDIR)
return (ENOTDIR);
- /* Need search permission in the directory. */
+ /*
+ * Need search permission in the directory.
+ */
error = smbfs_access(dvp, VEXEC, 0, cr, ct);
if (error)
return (error);
@@ -1288,13 +1363,35 @@ smbfslookup(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr, int dnlc,
return (0);
}
-#ifdef NOT_YET
- if (dnlc) {
/*
- * NOTE: search the dnlc here
+ * Now some sanity checks on the name.
+ * First check the length.
*/
+ if (nmlen > supplen)
+ return (ENAMETOOLONG);
+
+ /*
+ * Avoid surprises with characters that are
+ * illegal in Windows file names.
+ * Todo: CATIA mappings XXX
+ */
+ ill = illegal_chars;
+ if (dnp->n_flag & N_XATTR)
+ ill++; /* allow colon */
+ if (strpbrk(nm, ill))
+ return (EINVAL);
+
+#ifdef USE_DNLC
+ if (dnlc) {
+ /*
+ * Lookup this name in the DNLC. If there was a valid entry,
+ * then return the results of the lookup.
+ */
+ error = smbfslookup_dnlc(dvp, nm, vpp, cr);
+ if (error || *vpp != NULL)
+ return (error);
}
-#endif
+#endif /* USE_DNLC */
/*
* Handle lookup of ".." which is quite tricky,
@@ -1327,6 +1424,15 @@ smbfslookup(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr, int dnlc,
}
/*
+ * Special case for XATTR directory
+ */
+ if (dvp->v_flag & V_XATTRDIR) {
+ error = smbfs_xa_parent(dvp, vpp);
+ /* Intentionally no dnlc_update */
+ return (error);
+ }
+
+ /*
* Find the parent path length.
*/
rplen = dnp->n_rplen;
@@ -1344,11 +1450,12 @@ smbfslookup(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr, int dnlc,
}
vp = smbfs_make_node(dvp->v_vfsp,
dnp->n_rpath, rplen,
- NULL, 0, NULL);
- if (vp == NULL) {
- return (ENOENT);
- }
+ NULL, 0, 0, NULL);
+ ASSERT(vp);
vp->v_type = VDIR;
+#ifdef USE_DNLC
+ dnlc_update(dvp, nm, vp);
+#endif
/* Success! */
*vpp = vp;
@@ -1366,6 +1473,10 @@ smbfslookup(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr, int dnlc,
/* Note: this can allocate a new "name" */
error = smbfs_smb_lookup(dnp, &name, &nmlen, &fa, &scred);
smb_credrele(&scred);
+#ifdef USE_DNLC
+ if (error == ENOENT)
+ dnlc_enter(dvp, nm, DNLC_NO_VNODE);
+#endif
if (error)
goto out;
@@ -1376,6 +1487,10 @@ smbfslookup(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr, int dnlc,
if (error)
goto out;
+#ifdef USE_DNLC
+ dnlc_update(dvp, nm, vp);
+#endif
+
/* Success! */
*vpp = vp;
@@ -1387,6 +1502,84 @@ out:
return (error);
}
+#ifdef USE_DNLC
+#ifdef DEBUG
+static int smbfs_lookup_dnlc_hits = 0;
+static int smbfs_lookup_dnlc_misses = 0;
+static int smbfs_lookup_dnlc_neg_hits = 0;
+static int smbfs_lookup_dnlc_disappears = 0;
+static int smbfs_lookup_dnlc_lookups = 0;
+#endif
+
+/* ARGSUSED */
+static int
+smbfslookup_dnlc(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr)
+{
+ int error;
+ vnode_t *vp;
+ struct vattr va;
+ smbnode_t *dnp;
+
+ dnp = VTOSMB(dvp);
+
+ ASSERT(*nm != '\0');
+ ASSERT(curproc->p_zone == VTOSMI(dvp)->smi_zone);
+
+ /*
+ * Lookup this name in the DNLC. If successful, then validate
+ * the caches and then recheck the DNLC. The DNLC is rechecked
+ * just in case this entry got invalidated during the call
+ * to smbfsgetattr().
+ * An assumption is being made that it is safe to say that a
+ * file exists which may not on the server. Any operations to
+ * the server will fail with ESTALE.
+ */
+
+#ifdef DEBUG
+ smbfs_lookup_dnlc_lookups++;
+#endif
+ vp = dnlc_lookup(dvp, nm);
+ if (vp != NULL) {
+ if (vp == DNLC_NO_VNODE && !vn_is_readonly(dvp))
+ smbfs_attr_cacheremove(dnp);
+ VN_RELE(vp);
+ error = smbfsgetattr(dvp, &va, cr);
+ if (error)
+ return (error);
+ vp = dnlc_lookup(dvp, nm);
+ if (vp != NULL) {
+ /*
+ * NFS checks VEXEC access here,
+ * but we've already done that
+ * in the caller.
+ */
+ if (vp == DNLC_NO_VNODE) {
+ VN_RELE(vp);
+#ifdef DEBUG
+ smbfs_lookup_dnlc_neg_hits++;
+#endif
+ return (ENOENT);
+ }
+ *vpp = vp;
+#ifdef DEBUG
+ smbfs_lookup_dnlc_hits++;
+#endif
+ return (0);
+ }
+#ifdef DEBUG
+ smbfs_lookup_dnlc_disappears++;
+#endif
+ }
+#ifdef DEBUG
+ else
+ smbfs_lookup_dnlc_misses++;
+#endif
+ *vpp = NULL;
+
+ return (0);
+}
+#endif /* USE_DNLC */
+
/*
* XXX
* vsecattr_t is new to build 77, and we need to eventually support
@@ -1417,6 +1610,7 @@ smbfs_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive,
int nmlen = strlen(nm);
uint32_t disp;
uint16_t fid;
+ int xattr;
vfsp = dvp->v_vfsp;
smi = VFTOSMI(vfsp);
@@ -1528,10 +1722,8 @@ smbfs_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive,
if (error)
goto out;
-#ifdef NOT_YET
- /* remove the entry from the negative entry from the dnlc */
- dnlc_remove(dvp, name);
-#endif
+ /* remove possible negative entry from the dnlc */
+ dnlc_remove(dvp, nm);
/*
* Now the code derived from Darwin,
@@ -1553,7 +1745,8 @@ smbfs_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive,
else
disp = NTCREATEX_DISP_OPEN_IF;
}
- error = smbfs_smb_create(dnp, name, nmlen, &scred, &fid, disp, 0);
+ xattr = (dnp->n_flag & N_XATTR) ? 1 : 0;
+ error = smbfs_smb_create(dnp, name, nmlen, &scred, &fid, disp, xattr);
if (error)
goto out;
@@ -1617,11 +1810,12 @@ smbfs_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive,
if (error)
goto out;
-#ifdef NOT_YET
- dnlc_update(dvp, name, vp);
- /* XXX invalidate pages if we truncated? */
+#ifdef USE_DNLC
+ dnlc_update(dvp, nm, vp);
#endif
+ /* XXX invalidate pages if we truncated? */
+
/* Success! */
*vpp = vp;
error = 0;
@@ -1688,7 +1882,6 @@ smbfs_remove(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct,
goto out;
}
-#ifdef NOT_YET
/*
* First just remove the entry from the name cache, as it
* is most likely the only entry for this vp.
@@ -1703,7 +1896,6 @@ smbfs_remove(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct,
*/
if (vp->v_count > 1)
dnlc_purge_vp(vp);
-#endif /* NOT_YET */
/*
* Now we have the real reference count on the vnode
@@ -1721,7 +1913,6 @@ smbfs_remove(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct,
*/
mutex_exit(&np->r_statelock);
error = EBUSY;
- goto out;
} else {
mutex_exit(&np->r_statelock);
@@ -1778,6 +1969,7 @@ smbfsrename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr,
vnode_t *nvp = NULL;
vnode_t *ovp = NULL;
smbnode_t *onp;
+ smbnode_t *nnp;
smbnode_t *odnp;
smbnode_t *ndnp;
struct smb_cred scred;
@@ -1897,7 +2089,6 @@ smbfsrename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr,
goto out;
}
-#ifdef NOT_YET
/*
* Purge the name cache of all references to this vnode
* so that we can check the reference count to infer
@@ -1916,26 +2107,69 @@ smbfsrename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr,
*/
if (nvp->v_count > 1)
dnlc_purge_vp(nvp);
-#endif
+ /*
+ * when renaming directories to be a subdirectory of a
+ * different parent, the dnlc entry for ".." will no
+ * longer be valid, so it must be removed
+ */
+ if (ndvp != odvp) {
+ if (ovp->v_type == VDIR) {
+ dnlc_remove(ovp, "..");
+ }
+ }
+
+ /*
+ * CIFS gives a SHARING_VIOLATION error when
+ * trying to rename onto an exising object,
+ * so try to remove the target first.
+ * (Only for files, not directories.)
+ */
+ if (nvp->v_type == VDIR) {
+ error = EEXIST;
+ goto out;
+ }
- if (nvp->v_count > 1 && nvp->v_type != VDIR) {
+ /*
+ * Nodes that are "not active" here appear to have
+ * v_count=2 (should be 1. XXX investigate later)
+ * Code here is similar to smbfs_remove.
+ */
+ nnp = VTOSMB(nvp);
+ mutex_enter(&nnp->r_statelock);
+ if (nvp->v_count > 2) {
/*
* The target file exists, is not the same as
* the source file, and is active. Other FS
* implementations unlink the target here.
* For SMB, we don't assume we can remove an
* open file. Return an error instead.
- * Darwin returned an error here too.
*/
- error = EEXIST;
+ mutex_exit(&nnp->r_statelock);
+ error = EBUSY;
goto out;
}
+ mutex_exit(&nnp->r_statelock);
+
+ /*
+ * Target file is not active. Try to remove it.
+ */
+ smb_credinit(&scred, curproc, cr);
+ error = smbfs_smb_delete(nnp, &scred, NULL, 0, 0);
+ smb_credrele(&scred);
+ if (error)
+ goto out;
+ /*
+ * OK, removed the target file. Continue as if
+ * lookup target had failed (nvp == NULL).
+ */
+ vn_vfsunlock(nvp);
+ nvp_locked = 0;
+ VN_RELE(nvp);
+ nvp = NULL;
} /* nvp */
-#ifdef NOT_YET
dnlc_remove(odvp, onm);
dnlc_remove(ndvp, nnm);
-#endif
onp = VTOSMB(ovp);
smb_credinit(&scred, curproc, cr);
@@ -1990,6 +2224,10 @@ smbfs_mkdir(vnode_t *dvp, char *nm, struct vattr *va, vnode_t **vpp,
(nmlen == 2 && name[0] == '.' && name[1] == '.'))
return (EEXIST);
+ /* Only plain files are allowed in V_XATTRDIR. */
+ if (dvp->v_flag & V_XATTRDIR)
+ return (EINVAL);
+
if (smbfs_rw_enter_sig(&dnp->r_rwlock, RW_WRITER, SMBINTR(dvp)))
return (EINTR);
smb_credinit(&scred, curproc, cr);
@@ -2006,6 +2244,9 @@ smbfs_mkdir(vnode_t *dvp, char *nm, struct vattr *va, vnode_t **vpp,
if (error)
goto out;
+ /* remove possible negative entry from the dnlc */
+ dnlc_remove(dvp, nm);
+
error = smbfs_smb_mkdir(dnp, name, nmlen, &scred);
if (error)
goto out;
@@ -2020,8 +2261,8 @@ smbfs_mkdir(vnode_t *dvp, char *nm, struct vattr *va, vnode_t **vpp,
if (error)
goto out;
-#ifdef NOT_YET
- dnlc_update(dvp, name, vp);
+#ifdef USE_DNLC
+ dnlc_update(dvp, nm, vp);
#endif
if (name[0] == '.')
@@ -2108,6 +2349,27 @@ smbfs_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr,
goto out;
}
+ /*
+ * First just remove the entry from the name cache, as it
+ * is most likely an entry for this vp.
+ */
+ dnlc_remove(dvp, nm);
+
+ /*
+ * If there vnode reference count is greater than one, then
+ * there may be additional references in the DNLC which will
+ * need to be purged. First, trying removing the entry for
+ * the parent directory and see if that removes the additional
+ * reference(s). If that doesn't do it, then use dnlc_purge_vp
+ * to completely remove any references to the directory which
+ * might still exist in the DNLC.
+ */
+ if (vp->v_count > 1) {
+ dnlc_remove(vp, "..");
+ if (vp->v_count > 1)
+ dnlc_purge_vp(vp);
+ }
+
error = smbfs_smb_rmdir(np, &scred);
if (error)
goto out;
@@ -2116,10 +2378,6 @@ smbfs_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr,
dnp->n_flag |= NMODIFIED;
mutex_exit(&np->r_statelock);
smbfs_attr_touchdir(dnp);
-#ifdef NOT_YET
- dnlc_remove(dvp, nm);
- dnlc_purge_vp(vp);
-#endif
smb_rmhash(np);
out:
@@ -2466,6 +2724,7 @@ smbfs_space(vnode_t *vp, int cmd, struct flock64 *bfp, int flag,
if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
return (EIO);
+ /* Caller (fcntl) has checked v_type */
ASSERT(vp->v_type == VREG);
if (cmd != F_FREESP)
return (EINVAL);
@@ -2506,10 +2765,12 @@ static int
smbfs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
caller_context_t *ct)
{
+ vfs_t *vfs;
smbmntinfo_t *smi;
struct smb_share *ssp;
- smi = VTOSMI(vp);
+ vfs = vp->v_vfsp;
+ smi = VFTOSMI(vfs);
if (curproc->p_zone != smi->smi_zone)
return (EIO);
@@ -2541,10 +2802,16 @@ smbfs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
break;
case _PC_SYMLINK_MAX: /* No symlinks until we do Unix extensions */
- case _PC_XATTR_EXISTS: /* No xattrs yet */
*valp = 0;
break;
+ case _PC_XATTR_EXISTS:
+ if (vfs->vfs_flag & VFS_XATTR) {
+ *valp = smbfs_xa_exists(vp, cr);
+ break;
+ }
+ return (EINVAL);
+
default:
return (fs_pathconf(vp, cmd, valp, cr, ct));
}
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_xattr.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_xattr.c
new file mode 100644
index 0000000000..19cfc44282
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_xattr.c
@@ -0,0 +1,491 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Functions supporting Solaris Extended Attributes,
+ * used to provide access to CIFS "named streams".
+ */
+
+#include <sys/systm.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/dnlc.h>
+#include <sys/vfs_opreg.h>
+#include <sys/u8_textprep.h>
+
+#include <netsmb/smb_osdep.h>
+#include <netsmb/smb.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>
+
+/*
+ * Solaris wants there to be a directory node to contain
+ * all the extended attributes. The SMB protocol does not
+ * really support a directory here, and uses very different
+ * operations to list attributes, etc. so we "fake up" an
+ * smbnode here to represent the attributes directory.
+ *
+ * We need to give this (fake) directory a unique identity,
+ * and since we're using the full remote pathname as the
+ * unique identity of all nodes, the easiest thing to do
+ * here is append a colon (:) to the given pathname.
+ *
+ * There are several places where smbfs_fullpath and its
+ * callers must decide what separator to use when building
+ * a remote path name, and the rule is now as follows:
+ * 1: When no XATTR involved, use "\\" as the separator.
+ * 2: Traversal into the (fake) XATTR dir adds one ":"
+ * 3: Children of the XATTR dir add nothing (sep=0)
+ * The result should be _one_ colon before the attr name.
+ */
+
+/* ARGSUSED */
+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);
+
+ xvp = smbfs_make_node(pvp->v_vfsp,
+ pnp->n_rpath, pnp->n_rplen,
+ NULL, 0, ':', NULL);
+ ASSERT(xvp);
+ /* Note: xvp has a VN_HOLD, which our caller expects. */
+ xnp = VTOSMB(xvp);
+
+ /* If it's a new node, initialize. */
+ 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);
+ }
+
+ /* Success! */
+ *vpp = xvp;
+ return (0);
+}
+
+/*
+ * Find the parent of an XATTR directory or file,
+ * by trimming off the ":attrname" part of rpath.
+ * Called on XATTR files to get the XATTR dir, and
+ * called on the XATTR dir to get the real object
+ * under which the (faked up) XATTR dir lives.
+ */
+int
+smbfs_xa_parent(vnode_t *vp, vnode_t **vpp)
+{
+ smbnode_t *np = VTOSMB(vp);
+ vnode_t *pvp;
+ int rplen;
+
+ if ((np->n_flag & N_XATTR) == 0)
+ return (EINVAL);
+
+ if (vp->v_flag & V_XATTRDIR) {
+ /*
+ * Want the parent of the XATTR directory.
+ * That's easy: just remove trailing ":"
+ */
+ 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 {
+ /*
+ * Want the XATTR directory given
+ * one of its XATTR files (children).
+ * Find the ":" and trim after it.
+ */
+ for (rplen = 1; rplen < np->n_rplen; rplen++)
+ if (np->n_rpath[rplen] == ':')
+ break;
+ /* Should have found ":stream_name" */
+ if (rplen >= np->n_rplen) {
+ SMBVDEBUG("colon not found");
+ return (ENOENT);
+ }
+ rplen++; /* keep the ":" */
+ if (rplen >= np->n_rplen) {
+ SMBVDEBUG("no stream name");
+ return (ENOENT);
+ }
+ }
+
+ pvp = smbfs_make_node(vp->v_vfsp,
+ np->n_rpath, rplen,
+ NULL, 0, 0, NULL);
+ ASSERT(pvp);
+
+ /* Note: pvp has a VN_HOLD from _make_node */
+ *vpp = pvp;
+ return (0);
+}
+
+/*
+ * This is called by smbfs_pathconf to find out
+ * if some file has any extended attributes.
+ * There's no short-cut way to find out, so we
+ * just list the attributes the usual way and
+ * check for an empty result.
+ *
+ * Returns 1: (exists) or 0: (none found)
+ */
+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;
+
+ /* Get the xattr dir */
+ error = smbfs_get_xattrdir(vp, &xvp, cr, LOOKUP_XATTR);
+ if (error)
+ return (0);
+ /* NB: have VN_HOLD on xpv */
+ xnp = VTOSMB(xvp);
+
+ smb_credinit(&scred, curproc, 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;
+
+ /* Have at least one named stream. */
+ SMBVDEBUG("ctx.f_name: %s\n", ctx.f_name);
+ rc = 1;
+
+out:
+ /* NB: Always call findclose, error or not. */
+ (void) smbfs_xa_findclose(&ctx);
+ smb_credrele(&scred);
+ VN_RELE(xvp);
+ return (rc);
+}
+
+
+/*
+ * This is called to get attributes (size, etc.) of either
+ * the "faked up" XATTR directory or a named stream.
+ */
+int
+smbfs_xa_getfattr(struct smbnode *xnp, struct smbfattr *fap,
+ struct smb_cred *scrp)
+{
+ vnode_t *xvp; /* xattr */
+ vnode_t *pvp; /* parent */
+ smbnode_t *pnp; /* parent */
+ int error, nlen;
+ const char *name, *sname;
+
+ xvp = SMBTOV(xnp);
+
+ /*
+ * Simulate smbfs_smb_getfattr() for a named stream.
+ * OK to leave a,c,m times zero (expected w/ XATTR).
+ * The XATTR directory is easy (all fake).
+ */
+ if (xvp->v_flag & V_XATTRDIR) {
+ fap->fa_attr = SMB_FA_DIR;
+ fap->fa_size = DEV_BSIZE;
+ return (0);
+ }
+
+ /*
+ * Do a lookup in the XATTR directory,
+ * using the stream name (last part)
+ * from the xattr node.
+ */
+ error = smbfs_xa_parent(xvp, &pvp);
+ if (error)
+ return (error);
+ /* Note: pvp has a VN_HOLD */
+ pnp = VTOSMB(pvp);
+
+ /* Get stream name (ptr and length) */
+ ASSERT(xnp->n_rplen > pnp->n_rplen);
+ nlen = xnp->n_rplen - pnp->n_rplen;
+ name = xnp->n_rpath + pnp->n_rplen;
+ sname = name;
+
+ /* Note: this can allocate a new "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);
+}
+
+/*
+ * Fetch the entire attribute list here in findopen.
+ * Will parse the results in findnext.
+ *
+ * This is called on the XATTR directory, so we
+ * have to get the (real) parent object first.
+ */
+/* ARGSUSED */
+int
+smbfs_xa_findopen(struct smbfs_fctx *ctx, struct smbnode *dnp,
+ const char *wildcard, int wclen)
+{
+ vnode_t *pvp; /* parent */
+ smbnode_t *pnp;
+ struct smb_t2rq *t2p;
+ struct smb_vc *vcp = SSTOVC(ctx->f_ssp);
+ struct mbchain *mbp;
+ int error;
+
+ ASSERT(dnp->n_flag & N_XATTR);
+
+ ctx->f_type = ft_XA;
+
+ error = smbfs_xa_parent(SMBTOV(dnp), &pvp);
+ if (error)
+ return (error);
+ ASSERT(pvp);
+ /* Note: pvp has a VN_HOLD */
+ pnp = VTOSMB(pvp);
+
+ if (ctx->f_t2) {
+ smb_t2_done(ctx->f_t2);
+ ctx->f_t2 = NULL;
+ }
+
+ error = smb_t2_alloc(SSTOCP(ctx->f_ssp),
+ SMB_TRANS2_QUERY_PATH_INFORMATION,
+ ctx->f_scred, &t2p);
+ if (error)
+ goto out;
+ ctx->f_t2 = t2p;
+
+ mbp = &t2p->t2_tparam;
+ mb_init(mbp);
+ mb_put_uint16le(mbp, SMB_QFILEINFO_STREAM_INFO);
+ mb_put_uint32le(mbp, 0);
+ error = smbfs_fullpath(mbp, vcp, pnp, NULL, NULL, 0);
+ if (error)
+ goto out;
+ t2p->t2_maxpcount = 2;
+ t2p->t2_maxdcount = INT16_MAX;
+ error = smb_t2_request(t2p);
+ if (error) {
+ if (smb_t2_err(t2p) == NT_STATUS_INVALID_PARAMETER)
+ error = ENOTSUP;
+ }
+ /*
+ * No returned parameters to parse.
+ * Returned data are in t2_rdata,
+ * which we'll parse in _findnext.
+ * However, save the wildcard.
+ */
+ ctx->f_wildcard = wildcard;
+ ctx->f_wclen = wclen;
+
+out:
+ VN_RELE(pvp);
+ return (error);
+}
+
+/*
+ * Get the next name in an XATTR directory into f_name
+ */
+/* ARGSUSED */
+int
+smbfs_xa_findnext(struct smbfs_fctx *ctx, uint16_t limit)
+{
+ struct mdchain *mdp;
+ struct smb_t2rq *t2p;
+ uint32_t size, next;
+ uint64_t llongint;
+ int error, skip, used, nmlen;
+
+ t2p = ctx->f_t2;
+ mdp = &t2p->t2_rdata;
+
+ if (ctx->f_flags & SMBFS_RDD_FINDSINGLE) {
+ ASSERT(ctx->f_wildcard);
+ SMBVDEBUG("wildcard: %s\n", ctx->f_wildcard);
+ }
+
+again:
+ if (ctx->f_flags & SMBFS_RDD_EOF)
+ return (ENOENT);
+
+ /* Parse FILE_STREAM_INFORMATION */
+ if ((error = md_get_uint32le(mdp, &next)) != 0) /* offset to */
+ return (ENOENT);
+ if ((error = md_get_uint32le(mdp, &size)) != 0) /* name len */
+ return (ENOENT);
+ md_get_uint64le(mdp, &llongint); /* file size */
+ ctx->f_attr.fa_size = llongint;
+ md_get_uint64le(mdp, NULL); /* alloc. size */
+ used = 4 + 4 + 8 + 8; /* how much we consumed */
+
+ /*
+ * Copy the string, but skip the first char (":")
+ * Watch out for zero-length strings here.
+ */
+ if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp))) {
+ if (size >= 2) {
+ size -= 2; used += 2;
+ md_get_uint16le(mdp, NULL);
+ }
+ nmlen = min(size, SMB_MAXFNAMELEN * 2);
+ } else {
+ if (size >= 1) {
+ size -= 1; used += 1;
+ md_get_uint8(mdp, NULL);
+ }
+ nmlen = min(size, SMB_MAXFNAMELEN);
+ }
+
+ if (ctx->f_name)
+ kmem_free(ctx->f_name, ctx->f_namesz);
+ ctx->f_nmlen = nmlen;
+ /* Add one to prevent allocating size zero. */
+ ctx->f_namesz = nmlen + 1;
+ ctx->f_name = kmem_alloc(ctx->f_namesz, KM_SLEEP);
+ error = md_get_mem(mdp, ctx->f_name, nmlen, MB_MSYSTEM);
+ if (error)
+ return (error);
+ used += nmlen;
+
+ /*
+ * Convert UCS-2 to UTF-8
+ */
+ smbfs_fname_tolocal(ctx);
+ if (nmlen)
+ SMBVDEBUG("name: %s\n", ctx->f_name);
+ else
+ SMBVDEBUG("null name!\n");
+
+ /*
+ * Skip padding until next offset
+ */
+ if (next > used) {
+ skip = next - used;
+ md_get_mem(mdp, NULL, skip, MB_MSYSTEM);
+ }
+ if (next == 0)
+ ctx->f_flags |= SMBFS_RDD_EOF;
+
+ /*
+ * Chop off the trailing ":$DATA"
+ * The 6 here is strlen(":$DATA")
+ */
+ if (ctx->f_nmlen >= 6) {
+ char *p = ctx->f_name + ctx->f_nmlen - 6;
+ if (strncmp(p, ":$DATA", 6) == 0) {
+ *p = '\0'; /* Chop! */
+ ctx->f_nmlen -= 6;
+ }
+ }
+
+ /*
+ * The Chop above will typically leave
+ * an empty name in the first slot,
+ * which we will skip here.
+ */
+ if (ctx->f_nmlen == 0)
+ goto again;
+
+ /*
+ * If this is a lookup of a specific name,
+ * skip past any non-matching names.
+ */
+ 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);
+}
+
+/*
+ * Find first/next/close for XATTR directories.
+ * NB: also used by smbfs_smb_lookup
+ */
+
+int
+smbfs_xa_findclose(struct smbfs_fctx *ctx)
+{
+
+ if (ctx->f_name)
+ kmem_free(ctx->f_name, ctx->f_namesz);
+ if (ctx->f_t2)
+ smb_t2_done(ctx->f_t2);
+
+ return (0);
+}