4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
27 * Functions supporting Solaris Extended Attributes,
28 * used to provide access to CIFS "named streams".
31 #include <sys/systm.h>
33 #include <sys/vnode.h>
35 #include <sys/filio.h>
37 #include <sys/dirent.h>
38 #include <sys/errno.h>
39 #include <sys/sysmacros.h>
42 #include <sys/cmn_err.h>
43 #include <sys/u8_textprep.h>
45 #include <netsmb/smb_osdep.h>
46 #include <netsmb/smb.h>
47 #include <netsmb/smb_conn.h>
48 #include <netsmb/smb_subr.h>
49 #include <netsmb/smb_rq.h>
51 #include <smbfs/smbfs.h>
52 #include <smbfs/smbfs_node.h>
53 #include <smbfs/smbfs_subr.h>
55 #include <sys/fs_subr.h>
58 * Solaris wants there to be a directory node to contain
59 * all the extended attributes. The SMB protocol does not
60 * really support a directory here, and uses very different
61 * operations to list attributes, etc. so we "fake up" an
62 * smbnode here to represent the attributes directory.
64 * We need to give this (fake) directory a unique identity,
65 * and since we're using the full remote pathname as the
66 * unique identity of all nodes, the easiest thing to do
67 * here is append a colon (:) to the given pathname.
69 * There are several places where smbfs_fullpath and its
70 * callers must decide what separator to use when building
71 * a remote path name, and the rule is now as follows:
72 * 1: When no XATTR involved, use "\\" as the separator.
73 * 2: Traversal into the (fake) XATTR dir adds one ":"
74 * 3: Children of the XATTR dir add nothing (sep=0)
75 * The result should be _one_ colon before the attr name.
80 smbfs_get_xattrdir(vnode_t
*pvp
, vnode_t
**vpp
, cred_t
*cr
, int flags
)
88 * We don't allow recursive extended attributes
89 * (xattr under xattr dir.) so the "parent" node
90 * (pnp) must NOT be an XATTR directory or file.
92 if (pnp
->n_flag
& N_XATTR
)
95 xnp
= smbfs_node_findcreate(pnp
->n_mount
,
96 pnp
->n_rpath
, pnp
->n_rplen
, NULL
, 0, ':',
97 &smbfs_fattr0
); /* force create */
100 /* Note: xvp has a VN_HOLD, which our caller expects. */
102 /* If it's a new node, initialize. */
103 if (xvp
->v_type
== VNON
) {
105 mutex_enter(&xvp
->v_lock
);
107 xvp
->v_flag
|= V_XATTRDIR
;
108 mutex_exit(&xvp
->v_lock
);
110 mutex_enter(&xnp
->r_statelock
);
111 xnp
->n_flag
|= N_XATTR
;
112 mutex_exit(&xnp
->r_statelock
);
121 * Find the parent of an XATTR directory or file,
122 * by trimming off the ":attrname" part of rpath.
123 * Called on XATTR files to get the XATTR dir, and
124 * called on the XATTR dir to get the real object
125 * under which the (faked up) XATTR dir lives.
128 smbfs_xa_parent(vnode_t
*vp
, vnode_t
**vpp
)
130 smbnode_t
*np
= VTOSMB(vp
);
136 if ((np
->n_flag
& N_XATTR
) == 0)
139 if (vp
->v_flag
& V_XATTRDIR
) {
141 * Want the parent of the XATTR directory.
142 * That's easy: just remove trailing ":"
144 rplen
= np
->n_rplen
- 1;
146 SMBVDEBUG("rplen < 1?");
149 if (np
->n_rpath
[rplen
] != ':') {
150 SMBVDEBUG("last is not colon");
155 * Want the XATTR directory given
156 * one of its XATTR files (children).
157 * Find the ":" and trim after it.
159 for (rplen
= 1; rplen
< np
->n_rplen
; rplen
++)
160 if (np
->n_rpath
[rplen
] == ':')
162 /* Should have found ":stream_name" */
163 if (rplen
>= np
->n_rplen
) {
164 SMBVDEBUG("colon not found");
167 rplen
++; /* keep the ":" */
168 if (rplen
>= np
->n_rplen
) {
169 SMBVDEBUG("no stream name");
174 pnp
= smbfs_node_findcreate(np
->n_mount
,
175 np
->n_rpath
, rplen
, NULL
, 0, 0,
176 &smbfs_fattr0
); /* force create */
178 /* Note: have VN_HOLD from smbfs_node_findcreate */
184 * This is called by smbfs_pathconf to find out
185 * if some file has any extended attributes.
186 * There's no short-cut way to find out, so we
187 * just list the attributes the usual way and
188 * check for an empty result.
190 * Returns 1: (exists) or 0: (none found)
193 smbfs_xa_exists(vnode_t
*vp
, cred_t
*cr
)
197 struct smb_cred scred
;
198 struct smbfs_fctx ctx
;
201 /* Get the xattr dir */
202 error
= smbfs_get_xattrdir(vp
, &xvp
, cr
, LOOKUP_XATTR
);
205 /* NB: have VN_HOLD on xpv */
208 smb_credinit(&scred
, cr
);
210 bzero(&ctx
, sizeof (ctx
));
211 ctx
.f_flags
= SMBFS_RDD_FINDFIRST
;
213 ctx
.f_scred
= &scred
;
214 ctx
.f_ssp
= xnp
->n_mount
->smi_share
;
216 error
= smbfs_xa_findopen(&ctx
, xnp
, "*", 1);
220 error
= smbfs_xa_findnext(&ctx
, 1);
224 /* Have at least one named stream. */
225 SMBVDEBUG("ctx.f_name: %s\n", ctx
.f_name
);
229 /* NB: Always call findclose, error or not. */
230 (void) smbfs_xa_findclose(&ctx
);
231 smb_credrele(&scred
);
238 * This is called to get attributes (size, etc.) of either
239 * the "faked up" XATTR directory or a named stream.
242 smbfs_xa_getfattr(struct smbnode
*xnp
, struct smbfattr
*fap
,
243 struct smb_cred
*scrp
)
245 vnode_t
*xvp
; /* xattr */
246 vnode_t
*pvp
; /* parent */
247 smbnode_t
*pnp
; /* parent */
249 const char *name
, *sname
;
254 * Simulate smbfs_smb_getfattr() for a named stream.
255 * OK to leave a,c,m times zero (expected w/ XATTR).
256 * The XATTR directory is easy (all fake).
258 if (xvp
->v_flag
& V_XATTRDIR
) {
259 fap
->fa_attr
= SMB_FA_DIR
;
260 fap
->fa_size
= DEV_BSIZE
;
265 * Do a lookup in the XATTR directory,
266 * using the stream name (last part)
267 * from the xattr node.
269 error
= smbfs_xa_parent(xvp
, &pvp
);
272 /* Note: pvp has a VN_HOLD */
275 /* Get stream name (ptr and length) */
276 ASSERT(xnp
->n_rplen
> pnp
->n_rplen
);
277 nlen
= xnp
->n_rplen
- pnp
->n_rplen
;
278 name
= xnp
->n_rpath
+ pnp
->n_rplen
;
281 /* Note: this can allocate a new "name" */
282 error
= smbfs_smb_lookup(pnp
, &name
, &nlen
, fap
, scrp
);
283 if (error
== 0 && name
!= sname
)
284 smbfs_name_free(name
, nlen
);
292 * Fetch the entire attribute list here in findopen.
293 * Will parse the results in findnext.
295 * This is called on the XATTR directory, so we
296 * have to get the (real) parent object first.
300 smbfs_xa_findopen(struct smbfs_fctx
*ctx
, struct smbnode
*dnp
,
301 const char *wildcard
, int wclen
)
303 vnode_t
*pvp
; /* parent */
305 struct smb_t2rq
*t2p
;
306 struct smb_vc
*vcp
= SSTOVC(ctx
->f_ssp
);
310 ASSERT(dnp
->n_flag
& N_XATTR
);
313 ctx
->f_namesz
= SMB_MAXFNAMELEN
+ 1;
314 if (SMB_UNICODE_STRINGS(SSTOVC(ctx
->f_ssp
)))
316 ctx
->f_name
= kmem_alloc(ctx
->f_namesz
, KM_SLEEP
);
318 error
= smbfs_xa_parent(SMBTOV(dnp
), &pvp
);
322 /* Note: pvp has a VN_HOLD */
326 smb_t2_done(ctx
->f_t2
);
330 error
= smb_t2_alloc(SSTOCP(ctx
->f_ssp
),
331 SMB_TRANS2_QUERY_PATH_INFORMATION
,
337 mbp
= &t2p
->t2_tparam
;
339 (void) mb_put_uint16le(mbp
, SMB_QFILEINFO_STREAM_INFO
);
340 (void) mb_put_uint32le(mbp
, 0);
341 error
= smbfs_fullpath(mbp
, vcp
, pnp
, NULL
, 0, 0);
344 t2p
->t2_maxpcount
= 2;
345 t2p
->t2_maxdcount
= INT16_MAX
;
346 error
= smb_t2_request(t2p
);
348 if (t2p
->t2_sr_error
== NT_STATUS_INVALID_PARAMETER
)
352 * No returned parameters to parse.
353 * Returned data are in t2_rdata,
354 * which we'll parse in _findnext.
355 * However, save the wildcard.
357 ctx
->f_wildcard
= wildcard
;
358 ctx
->f_wclen
= wclen
;
366 * Get the next name in an XATTR directory into f_name
370 smbfs_xa_findnext(struct smbfs_fctx
*ctx
, uint16_t limit
)
373 struct smb_t2rq
*t2p
;
376 int error
, skip
, used
, nmlen
;
379 mdp
= &t2p
->t2_rdata
;
381 if (ctx
->f_flags
& SMBFS_RDD_FINDSINGLE
) {
382 ASSERT(ctx
->f_wildcard
);
383 SMBVDEBUG("wildcard: %s\n", ctx
->f_wildcard
);
387 if (ctx
->f_flags
& SMBFS_RDD_EOF
)
390 /* Parse FILE_STREAM_INFORMATION */
391 if ((error
= md_get_uint32le(mdp
, &next
)) != 0) /* offset to */
393 if ((error
= md_get_uint32le(mdp
, &size
)) != 0) /* name len */
395 (void) md_get_uint64le(mdp
, &llongint
); /* file size */
396 ctx
->f_attr
.fa_size
= llongint
;
397 (void) md_get_uint64le(mdp
, NULL
); /* alloc. size */
398 used
= 4 + 4 + 8 + 8; /* how much we consumed */
401 * Copy the string, but skip the first char (":")
402 * Watch out for zero-length strings here.
404 if (SMB_UNICODE_STRINGS(SSTOVC(ctx
->f_ssp
))) {
406 size
-= 2; used
+= 2;
407 (void) md_get_uint16le(mdp
, NULL
);
409 nmlen
= min(size
, SMB_MAXFNAMELEN
* 2);
412 size
-= 1; used
+= 1;
413 (void) md_get_uint8(mdp
, NULL
);
415 nmlen
= min(size
, SMB_MAXFNAMELEN
);
418 ASSERT(nmlen
< ctx
->f_namesz
);
419 ctx
->f_nmlen
= nmlen
;
420 error
= md_get_mem(mdp
, ctx
->f_name
, nmlen
, MB_MSYSTEM
);
426 * Convert UCS-2 to UTF-8
428 smbfs_fname_tolocal(ctx
);
430 SMBVDEBUG("name: %s\n", ctx
->f_name
);
432 SMBVDEBUG("null name!\n");
435 * Skip padding until next offset
439 (void) md_get_mem(mdp
, NULL
, skip
, MB_MSYSTEM
);
442 ctx
->f_flags
|= SMBFS_RDD_EOF
;
445 * Chop off the trailing ":$DATA"
446 * The 6 here is strlen(":$DATA")
448 if (ctx
->f_nmlen
>= 6) {
449 char *p
= ctx
->f_name
+ ctx
->f_nmlen
- 6;
450 if (strncmp(p
, ":$DATA", 6) == 0) {
451 *p
= '\0'; /* Chop! */
457 * The Chop above will typically leave
458 * an empty name in the first slot,
459 * which we will skip here.
461 if (ctx
->f_nmlen
== 0)
465 * If this is a lookup of a specific name,
466 * skip past any non-matching names.
468 if (ctx
->f_flags
& SMBFS_RDD_FINDSINGLE
) {
469 if (ctx
->f_wclen
!= ctx
->f_nmlen
)
471 if (u8_strcmp(ctx
->f_wildcard
, ctx
->f_name
,
472 ctx
->f_nmlen
, U8_STRCMP_CI_LOWER
,
473 U8_UNICODE_LATEST
, &error
) || error
)
481 * Find first/next/close for XATTR directories.
482 * NB: also used by smbfs_smb_lookup
486 smbfs_xa_findclose(struct smbfs_fctx
*ctx
)
490 kmem_free(ctx
->f_name
, ctx
->f_namesz
);
492 smb_t2_done(ctx
->f_t2
);