Mercurial > illumos > illumos-gate
changeset 7040:eeae3015c35e
6668593 Support NT "named streams"
6720550 rename over existing file should attempt to remove
author | gwr |
---|---|
date | Fri, 04 Jul 2008 06:02:33 -0700 |
parents | 72bd9824e54d |
children | b4c5fe87fad8 |
files | usr/src/uts/common/Makefile.files usr/src/uts/common/fs/smbclnt/netsmb/smb_subrs.c usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.c usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c usr/src/uts/common/fs/smbclnt/smbfs/smbfs_xattr.c |
diffstat | 11 files changed, 1082 insertions(+), 339 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/uts/common/Makefile.files Fri Jul 04 05:26:40 2008 -0700 +++ b/usr/src/uts/common/Makefile.files Fri Jul 04 06:02:33 2008 -0700 @@ -1047,10 +1047,10 @@ 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 #
--- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_subrs.c Fri Jul 04 05:26:40 2008 -0700 +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_subrs.c Fri Jul 04 06:02:33 2008 -0700 @@ -373,6 +373,7 @@ {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 @@ case ERRbadformat: case ERRremcd: case ERRrmuns: + case ERRunknownlevel: return (EINVAL); case ERRbadfile: case ERRbadpath:
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.c Fri Jul 04 05:26:40 2008 -0700 +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.c Fri Jul 04 06:02:33 2008 -0700 @@ -224,7 +224,9 @@ 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 @@ 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 @@ * 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 @@ 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) { /*
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h Fri Jul 04 05:26:40 2008 -0700 +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h Fri Jul 04 06:02:33 2008 -0700 @@ -175,6 +175,7 @@ #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 @@ 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 */
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c Fri Jul 04 05:26:40 2008 -0700 +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c Fri Jul 04 06:02:33 2008 -0700 @@ -200,6 +200,14 @@ */ 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 @@ 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 @@ /* * 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 @@ 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 @@ 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 @@ /*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 @@ /*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 @@ ctx->f_eofs = next; ctx->f_ecnt--; ctx->f_left--; + + smbfs_fname_tolocal(ctx); return (0); } @@ -2825,7 +2660,7 @@ } 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 @@ 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_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; } - ctx->f_dnp = dnp; - ctx->f_flags = SMBFS_RDD_FINDFIRST; - ctx->f_scred = scrp; + 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 @@ 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 @@ 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 @@ 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
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c Fri Jul 04 05:26:40 2008 -0700 +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c Fri Jul 04 06:02:33 2008 -0700 @@ -289,12 +289,34 @@ 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
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h Fri Jul 04 05:26:40 2008 -0700 +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h Fri Jul 04 06:02:33 2008 -0700 @@ -78,6 +78,14 @@ 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 @@ #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 @@ /* * Setable values */ + smbfs_fctx_type_t f_type; int f_flags; /* SMBFS_RDD_ */ /* * Return values @@ -248,7 +257,7 @@ 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 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);
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c Fri Jul 04 05:26:40 2008 -0700 +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c Fri Jul 04 06:02:33 2008 -0700 @@ -174,6 +174,7 @@ int dirlen, const char *name, int nmlen, + char sep, struct smbfattr *fap) { char *rpath; @@ -189,26 +190,33 @@ /* * 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);
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c Fri Jul 04 05:26:40 2008 -0700 +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c Fri Jul 04 06:02:33 2008 -0700 @@ -80,12 +80,51 @@ 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 @@ * * 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 @@ 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 @@ 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 @@ 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 @@ * 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 @@ 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;
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c Fri Jul 04 05:26:40 2008 -0700 +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c Fri Jul 04 06:02:33 2008 -0700 @@ -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 @@ /* 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 @@ 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 @@ /* * 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,9 +384,15 @@ mutex_exit(&np->r_statelock); have_fid: + /* + * 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. */ va.va_mask = AT_MTIME; @@ -459,7 +490,15 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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; - - smi = VTOSMI(dvp); + smbnode_t *dnp; + int error; + + 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 @@ 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 @@ return (0); } - /* if the name is longer that what is supported, return an error */ - if (nmlen > supplen) - return (ENAMETOOLONG); + /* + * Can't do lookups in non-directories. + */ + if (dvp->v_type != VDIR) + return (ENOTDIR); /* - * Avoid surprises with characters that are - * illegal in Windows file names. - * Todo: CATIA mappings XXX + * Need search permission in the directory. */ - 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. */ error = smbfs_access(dvp, VEXEC, 0, cr, ct); if (error) return (error); @@ -1288,13 +1363,35 @@ return (0); } -#ifdef NOT_YET - if (dnlc) { + /* + * Now some sanity checks on the name. + * First check the length. + */ + if (nmlen > supplen) + return (ENAMETOOLONG); + /* - * NOTE: search the dnlc here + * 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 @@ } /* + * 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 @@ } 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 @@ /* 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 @@ if (error) goto out; +#ifdef USE_DNLC + dnlc_update(dvp, nm, vp); +#endif + /* Success! */ *vpp = vp; @@ -1387,6 +1502,84 @@ 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 @@ int nmlen = strlen(nm); uint32_t disp; uint16_t fid; + int xattr; vfsp = dvp->v_vfsp; smi = VFTOSMI(vfsp); @@ -1528,10 +1722,8 @@ 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 @@ 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,10 +1810,11 @@ if (error) goto out; -#ifdef NOT_YET - dnlc_update(dvp, name, vp); +#ifdef USE_DNLC + dnlc_update(dvp, nm, vp); +#endif + /* XXX invalidate pages if we truncated? */ -#endif /* Success! */ *vpp = vp; @@ -1688,7 +1882,6 @@ 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 @@ */ 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 @@ */ mutex_exit(&np->r_statelock); error = EBUSY; - goto out; } else { mutex_exit(&np->r_statelock); @@ -1778,6 +1969,7 @@ 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 @@ 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 @@ */ if (nvp->v_count > 1) dnlc_purge_vp(nvp); -#endif - - if (nvp->v_count > 1 && nvp->v_type != VDIR) { + /* + * 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; + } + + /* + * 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 @@ (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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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)); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_xattr.c Fri Jul 04 06:02:33 2008 -0700 @@ -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); +}