1/*
2 * Copyright (c) 2012-2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <sys/mount.h>
25#include <sys/vm.h>
26#include <sys/kauth.h>
27#include <sys/smb_apple.h>
28#include <sys/msfscc.h>
29
30#include <netsmb/smb.h>
31#include <netsmb/smb_2.h>
32#include <smbfs/smbfs.h>
33#include <netsmb/smb_rq.h>
34#include <netsmb/smb_rq_2.h>
35#include <netsmb/smb_conn.h>
36#include <smbfs/smbfs_subr.h>
37#include <smbfs/smbfs_subr_2.h>
38#include <smbfs/smbfs_attrlist.h>
39
40#define SMBFS_AVERAGE_NAME_SIZE   22
41#define AVERAGE_SMBDIRENTRY_SIZE  (8 + SMBFS_AVERAGE_NAME_SIZE + 4)
42
43/* Packing routines: */
44static int attrblksize(struct attrlist *attrlist);
45
46static char* mountpointname(struct mount *mp);
47
48static void packattrblk(struct attrblock *abp,
49                        struct smbmount *smp,
50                        struct vnode *vp,
51                        struct smbfs_fctx *ctx,
52                        struct vfs_context *context);
53
54static void packcommonattr(struct attrblock *abp,
55                           struct smbmount *smp,
56                           struct smbfs_fctx *ctx,
57                           struct vfs_context *context);
58
59static void packdirattr(struct attrblock *abp,
60                        struct vnode *vp,
61                        struct smbfs_fctx *ctx);
62
63static void packfileattr(struct attrblock *abp,
64                         struct smbmount *smp,
65                         struct smbfs_fctx *ctx,
66                         struct vfs_context *context);
67
68static void packnameattr(struct attrblock *abp, struct smbfs_fctx *ctx,
69                         const u_int8_t *name, size_t namelen);
70
71
72/*==================== Attribute list support routines ====================*/
73
74/*
75 * Calculate the total size of an attribute block.
76 */
77static int
78attrblksize(struct attrlist *attrlist)
79{
80	int size;
81	attrgroup_t a;
82	int sizeof_timespec;
83	boolean_t is_64_bit = proc_is64bit(current_proc());
84
85    if (is_64_bit)
86        sizeof_timespec = sizeof(struct user64_timespec);
87    else
88        sizeof_timespec = sizeof(struct user32_timespec);
89
90	DBG_ASSERT((attrlist->commonattr & ~ATTR_CMN_VALIDMASK) == 0);
91
92	DBG_ASSERT((attrlist->volattr & ~ATTR_VOL_VALIDMASK) == 0);
93
94	DBG_ASSERT((attrlist->dirattr & ~ATTR_DIR_VALIDMASK) == 0);
95
96	DBG_ASSERT((attrlist->fileattr & ~ATTR_FILE_VALIDMASK) == 0);
97
98	DBG_ASSERT((attrlist->forkattr & ~ATTR_FORK_VALIDMASK) == 0);
99
100	size = 0;
101
102	if ((a = attrlist->commonattr) != 0) {
103		if (a & ATTR_CMN_NAME) size += sizeof(struct attrreference);
104		if (a & ATTR_CMN_DEVID) size += sizeof(dev_t);
105		if (a & ATTR_CMN_FSID) size += sizeof(fsid_t);
106		if (a & ATTR_CMN_OBJTYPE) size += sizeof(fsobj_type_t);
107		if (a & ATTR_CMN_OBJTAG) size += sizeof(fsobj_tag_t);
108		if (a & ATTR_CMN_OBJID) size += sizeof(fsobj_id_t);
109		if (a & ATTR_CMN_OBJPERMANENTID) size += sizeof(fsobj_id_t);
110		if (a & ATTR_CMN_PAROBJID) size += sizeof(fsobj_id_t);
111		if (a & ATTR_CMN_SCRIPT) size += sizeof(text_encoding_t);
112		if (a & ATTR_CMN_CRTIME) size += sizeof_timespec;
113		if (a & ATTR_CMN_MODTIME) size += sizeof_timespec;
114		if (a & ATTR_CMN_CHGTIME) size += sizeof_timespec;
115		if (a & ATTR_CMN_ACCTIME) size += sizeof_timespec;
116		if (a & ATTR_CMN_BKUPTIME) size += sizeof_timespec;
117		if (a & ATTR_CMN_FNDRINFO) size += 32 * sizeof(u_int8_t);
118		if (a & ATTR_CMN_OWNERID) size += sizeof(uid_t);
119		if (a & ATTR_CMN_GRPID) size += sizeof(gid_t);
120		if (a & ATTR_CMN_ACCESSMASK) size += sizeof(u_int32_t);
121		if (a & ATTR_CMN_FLAGS) size += sizeof(u_int32_t);
122		if (a & ATTR_CMN_USERACCESS) size += sizeof(u_int32_t);
123		if (a & ATTR_CMN_FILEID) size += sizeof(u_int64_t);
124		if (a & ATTR_CMN_PARENTID) size += sizeof(u_int64_t);
125	}
126	if ((a = attrlist->dirattr) != 0) {
127		if (a & ATTR_DIR_LINKCOUNT) size += sizeof(u_int32_t);
128		if (a & ATTR_DIR_ENTRYCOUNT) size += sizeof(u_int32_t);
129		if (a & ATTR_DIR_MOUNTSTATUS) size += sizeof(u_int32_t);
130	}
131	if ((a = attrlist->fileattr) != 0) {
132		if (a & ATTR_FILE_LINKCOUNT) size += sizeof(u_int32_t);
133		if (a & ATTR_FILE_TOTALSIZE) size += sizeof(off_t);
134		if (a & ATTR_FILE_ALLOCSIZE) size += sizeof(off_t);
135		if (a & ATTR_FILE_IOBLOCKSIZE) size += sizeof(u_int32_t);
136		if (a & ATTR_FILE_CLUMPSIZE) size += sizeof(u_int32_t);
137		if (a & ATTR_FILE_DEVTYPE) size += sizeof(u_int32_t);
138		if (a & ATTR_FILE_DATALENGTH) size += sizeof(off_t);
139		if (a & ATTR_FILE_DATAALLOCSIZE) size += sizeof(off_t);
140		if (a & ATTR_FILE_RSRCLENGTH) size += sizeof(off_t);
141		if (a & ATTR_FILE_RSRCALLOCSIZE) size += sizeof(off_t);
142	}
143
144	return (size);
145}
146
147static char*
148mountpointname(struct mount *mp)
149{
150	size_t namelength = strlen(vfs_statfs(mp)->f_mntonname);
151	int foundchars = 0;
152	char *c;
153
154	if (namelength == 0)
155		return (NULL);
156
157	/*
158	 * Look backwards through the name string, looking for
159	 * the first slash encountered (which must precede the
160	 * last part of the pathname).
161	 */
162	for (c = vfs_statfs(mp)->f_mntonname + namelength - 1;
163	     namelength > 0; --c, --namelength) {
164		if (*c != '/') {
165			foundchars = 1;
166		} else if (foundchars) {
167			return (c + 1);
168		}
169	}
170
171	return (vfs_statfs(mp)->f_mntonname);
172}
173
174/*
175 * Pack cnode attributes into an attribute block.
176 */
177static void
178packattrblk(struct attrblock *abp,
179            struct smbmount *smp,
180            struct vnode *vp,
181            struct smbfs_fctx *ctx,
182            struct vfs_context *context)
183{
184	struct attrlist *attrlistp = abp->ab_attrlist;
185	uint32_t is_dir;
186    int error;
187    uint32_t stream_flags = 0;
188	attrgroup_t file_attr = attrlistp->fileattr;
189	attrgroup_t common_attr = attrlistp->commonattr;
190	struct smbnode *np = NULL;
191    uint32_t need_rsrc_fork = 0;
192    size_t afp_size = 0;
193    uint8_t	afp_info[60] = {0};
194    uint8_t	zero_finfo[32] = {0};
195	uio_t afp_uio = NULL;
196    SMBFID fid = 0;
197	struct timespec ts;
198    uint32_t rsrc_fork_from_cache = 0;
199    uint32_t finder_info_from_cache = 0;
200    uint32_t max_access_from_cache = 0;
201
202    /* Do we need Resource Fork info? */
203    is_dir = (ctx->f_attr.fa_attr & SMB_EFA_DIRECTORY) ? 1 : 0;
204    if ((!is_dir) &&
205        ((ATTR_FILE_TOTALSIZE & file_attr) ||
206         (ATTR_FILE_ALLOCSIZE & file_attr) ||
207         (ATTR_FILE_RSRCLENGTH & file_attr) ||
208         (ATTR_FILE_RSRCALLOCSIZE & file_attr))) {
209            need_rsrc_fork = 1;
210    }
211
212    /*
213     * We may already have all the meta data we need from Mac <-> Mac (not yet
214     * implemented) or dont need Resource Fork, Finder Info, or Max Access data.
215     * If we do have all the needed meta data, then just go update vnode caches
216     * if we have a vnode and then pack the return attribute block.
217     */
218    if (((need_rsrc_fork) && (ctx->f_attr.fa_valid_mask & FA_RSRC_FORK_VALID)) &&
219        ((ATTR_CMN_FNDRINFO & common_attr) && (ctx->f_attr.fa_valid_mask & FA_FINDERINFO_VALID)) &&
220        ((ATTR_CMN_USERACCESS & common_attr) && (ctx->f_attr.fa_valid_mask & FA_MAX_ACCESS_VALID))) {
221        goto update_caches;
222    }
223
224    /*
225     * Figure out what attributes we are missing and then go get them from
226     * either the vnode or from the server.
227     */
228
229    /*
230     * Do we have a vnode that already has the attributes we need?
231     */
232    if (vp != NULL) {
233        np = VTOSMB(vp);
234
235        /* Check cached Resource Fork info */
236        if ((need_rsrc_fork) &&
237            !(ctx->f_attr.fa_valid_mask & FA_RSRC_FORK_VALID)) {
238
239            lck_mtx_lock(&np->rfrkMetaLock);
240
241            if (np->rfrk_cache_timer != 0) {
242                /* Resource fork data is valid in vnode so use it */
243                rsrc_fork_from_cache = 1;
244                ctx->f_attr.fa_valid_mask |= FA_RSRC_FORK_VALID;
245                ctx->f_attr.fa_rsrc_size = np->rfrk_size;
246                ctx->f_attr.fa_rsrc_alloc = np->rfrk_alloc_size;
247            }
248
249            lck_mtx_unlock(&np->rfrkMetaLock);
250        }
251
252        /* Check cached Finder Info */
253        if ((ATTR_CMN_FNDRINFO & common_attr) &&
254            !(ctx->f_attr.fa_valid_mask & FA_FINDERINFO_VALID)) {
255
256            if (np->finfo_cache != 0) {
257                /* Finder Info data is valid in vnode so use it */
258                finder_info_from_cache = 1;
259                ctx->f_attr.fa_valid_mask |= FA_FINDERINFO_VALID;
260                bcopy(&np->finfo, ctx->f_attr.fa_finder_info, sizeof(u_int8_t) * 32);
261            }
262        }
263
264        /* Check cached Max Info */
265        if ((ATTR_CMN_USERACCESS & common_attr) &&
266            !(ctx->f_attr.fa_valid_mask & FA_MAX_ACCESS_VALID)) {
267            if (timespeccmp(&np->maxAccessRightChTime, &np->n_chtime, ==)) {
268
269                /* Max Access data is valid in vnode so use it */
270                max_access_from_cache = 1;
271                ctx->f_attr.fa_valid_mask |= FA_MAX_ACCESS_VALID;
272                ctx->f_attr.fa_max_access = np->maxAccessRights;
273            }
274        }
275    }
276
277    /* Are we still missing attribute information? */
278    if (((need_rsrc_fork) && !(ctx->f_attr.fa_valid_mask & FA_RSRC_FORK_VALID)) ||
279        ((ATTR_CMN_FNDRINFO & common_attr) && !(ctx->f_attr.fa_valid_mask & FA_FINDERINFO_VALID)) ||
280        ((ATTR_CMN_USERACCESS & common_attr) && !(ctx->f_attr.fa_valid_mask & FA_MAX_ACCESS_VALID))) {
281        /*
282        * This will get us
283        * 1) Resource Fork sizes
284        * 2) Whether Finder Info exists or not on the item
285        * 3) For SMB 2.x, gets the max access on the item
286        *
287        * If we have to ask the server for the resource fork info or the
288        * user access, do it now as this will also tell us if there is any
289        * Finder Info on the file or not. For SMB 2.x, it will also get us the
290        * max access which is used for ATTR_CMN_USERACCESS.
291        *
292        * Best case (SMB 2.x) - Just this one call because no Finder Info found
293        * Worst case (SMB 2.x) - This call and another call to read Finder Info
294        *
295        * Best case (SMB 1.x) - This call and 2 calls (Create/Read + Close) to
296        *                       read Finder Info which will get the max access
297        *                       for SMB 1.x
298        * Worst case (SMB 1.x) - This call and 2 calls (Create + Close) to get
299        *                        max access because there is no Finder Info
300        */
301        error = smbfs_smb_qstreaminfo(ctx->f_share, ctx->f_dnp,
302                                      ctx->f_LocalName, ctx->f_LocalNameLen,
303                                      SFM_RESOURCEFORK_NAME,
304                                      NULL, NULL,
305                                      &ctx->f_attr.fa_rsrc_size, &ctx->f_attr.fa_rsrc_alloc,
306                                      &stream_flags, &ctx->f_attr.fa_max_access,
307                                      context);
308
309         if ((!error) || (error == ENOATTR)) {
310             /* smbfs_smb_qstreaminfo worked */
311
312             /* Did we need Resource Fork info? */
313             if ((need_rsrc_fork) &&
314                 !(ctx->f_attr.fa_valid_mask & FA_RSRC_FORK_VALID)) {
315                 /*
316                  * Successfully got Resource Fork Info. Its either already in
317                  * f_attr or no Resource Fork was found
318                  */
319                 ctx->f_attr.fa_valid_mask |= FA_RSRC_FORK_VALID;
320
321                 if (stream_flags & SMB_NO_RESOURCE_FORK) {
322                     /* No Resource Fork, so set resource fork lengths to zero */
323                     ctx->f_attr.fa_rsrc_size = 0;
324                     ctx->f_attr.fa_rsrc_alloc = 0;
325                 }
326                 else {
327                     /* SMBDEBUG("%s rsrc fork from qstreaminfo\n", ctx->f_LocalName); */
328                 }
329             }
330
331             /* Did we need Finder Info? */
332             if ((ATTR_CMN_FNDRINFO & common_attr) &&
333                 !(ctx->f_attr.fa_valid_mask & FA_FINDERINFO_VALID)) {
334                 /*
335                  * Now we know if there is Finder Info or not on the item
336                  */
337                 if (stream_flags & SMB_NO_FINDER_INFO) {
338                     /* No Finder Info, so set Finder Info to all zeros */
339                     ctx->f_attr.fa_valid_mask |= FA_FINDERINFO_VALID;
340                     bcopy(&zero_finfo, ctx->f_attr.fa_finder_info,
341                           sizeof(u_int8_t) * 32);
342                 }
343             }
344
345             /* Did we need Max Access? */
346             if ((ATTR_CMN_USERACCESS & common_attr) &&
347                 !(ctx->f_attr.fa_valid_mask & FA_MAX_ACCESS_VALID)) {
348                 if (SSTOVC(ctx->f_share)->vc_flags & SMBV_SMB2) {
349                     /*
350                      * Only SMB 2.x can get max access from
351                      * smbfs_smb_qstreaminfo call
352                      */
353                     ctx->f_attr.fa_valid_mask |= FA_MAX_ACCESS_VALID;
354                 }
355             }
356         }
357         else {
358             /* Got some sort of error. This shouldn't happen */
359             SMBDEBUG("smbfs_smb_qstreaminfo failed %d for %s \n",
360                      error, ctx->f_LocalName);
361         }
362    }
363
364    /*
365     * Do we still need to get the Finder Info?  At this point we know
366     * (1) Either no vnode or the cached finder info is not present
367     * (2) smbfs_smb_qstreaminfo told us that there is Finder Info on the item
368     *
369     * <11615553> For SMB 1.x, we can NOT get both the Finder Info and max
370     * access at the same time. Windows based servers will often give
371     * "Unspecified error" when you do a CreateAndX with extended response (ie
372     * max access) combined with Read of a named stream.
373     */
374    if ((ATTR_CMN_FNDRINFO & common_attr) &&
375        !(ctx->f_attr.fa_valid_mask & FA_FINDERINFO_VALID)) {
376
377        do {
378            afp_uio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ);
379            if (afp_uio == NULL) {
380                SMBERROR("uio_create failed for %s \n", ctx->f_LocalName);
381                break;
382            }
383
384            error = uio_addiov(afp_uio, CAST_USER_ADDR_T(afp_info),
385                               sizeof(afp_info));
386            if (error) {
387                SMBERROR("uio_addiov failed for %s \n", ctx->f_LocalName);
388                break;
389            }
390
391            uio_setoffset(afp_uio, 0);
392
393            /* Open/Read/Close the Finder Info */
394            error = smbfs_smb_cmpd_create_read_close(ctx->f_share, ctx->f_dnp,
395                                                     ctx->f_LocalName, ctx->f_LocalNameLen,
396                                                     SFM_FINDERINFO_NAME, strlen(SFM_FINDERINFO_NAME),
397                                                     afp_uio, &afp_size,
398                                                     NULL,
399                                                     context);
400            if (!error) {
401                /* Successfully got Finder Info */
402                ctx->f_attr.fa_valid_mask |= FA_FINDERINFO_VALID;
403
404                /* Verify returned size */
405                if (afp_size != AFP_INFO_SIZE) {
406                    /* Could be a 0 size returned meaning no Finder Info */
407                    if (afp_size != 0) {
408                        /* SMBDEBUG("%s Finder Info size mismatch %ld != %d \n",
409                                 ctx->f_LocalName, afp_size, AFP_INFO_SIZE); */
410                    }
411                    bcopy(&zero_finfo, ctx->f_attr.fa_finder_info,
412                          sizeof(u_int8_t) * 32);
413                }
414                else {
415                    /* Correct size, so just copy it in */
416                    bcopy(&afp_info[AFP_INFO_FINDER_OFFSET],
417                          ctx->f_attr.fa_finder_info,
418                          sizeof(u_int8_t) * 32);
419                    /* SMBDEBUG("Finder Info 0x%x 0x%x 0x%x 0x%x for %s \n",
420                             afp_info[AFP_INFO_FINDER_OFFSET],
421                             afp_info[AFP_INFO_FINDER_OFFSET + 1],
422                             afp_info[AFP_INFO_FINDER_OFFSET + 2],
423                             afp_info[AFP_INFO_FINDER_OFFSET + 3],
424                             ctx->f_LocalName); */
425                }
426            }
427            else {
428                if (error != ENOENT) {
429                    SMBDEBUG("smbfs_smb_cmpd_create_read_close failed %d for %s \n",
430                             error, ctx->f_LocalName);
431                }
432            }
433
434            if (afp_uio) {
435                uio_free(afp_uio);
436            }
437        } while (0);
438    }
439
440    /* Do we still need Max Access and its SMB 1.x? */
441    if ((ATTR_CMN_USERACCESS & common_attr) &&
442        !(ctx->f_attr.fa_valid_mask & FA_MAX_ACCESS_VALID) &&
443        !(SSTOVC(ctx->f_share)->vc_flags & SMBV_SMB2))
444    {
445        error = smb1fs_smb_open_maxaccess(ctx->f_share, ctx->f_dnp,
446                                          ctx->f_LocalName, ctx->f_LocalNameLen,
447                                          &fid, &ctx->f_attr.fa_max_access,
448                                          context);
449        if (!error) {
450            ctx->f_attr.fa_valid_mask |= FA_MAX_ACCESS_VALID;
451        }
452
453        if (fid) {
454            (void)smbfs_smb_close(ctx->f_share, fid, context);
455        }
456    }
457
458update_caches:
459
460    /* Update vnodes caches if the data did not come from the vnode caches */
461    if (vp) {
462        np = VTOSMB(vp);
463
464        /* Do we have updated resource fork info? */
465        if ((rsrc_fork_from_cache == 0) &&
466            (need_rsrc_fork) &&
467            (ctx->f_attr.fa_valid_mask & FA_RSRC_FORK_VALID)) {
468
469            lck_mtx_lock(&np->rfrkMetaLock);
470            np->rfrk_size = ctx->f_attr.fa_rsrc_size;
471            np->rfrk_alloc_size = ctx->f_attr.fa_rsrc_alloc;
472            nanouptime(&ts);
473            np->rfrk_cache_timer = ts.tv_sec;
474            lck_mtx_unlock(&np->rfrkMetaLock);
475        }
476
477        /* Do we have updated Finder Info? */
478       if ((finder_info_from_cache == 0) &&
479            (ATTR_CMN_FNDRINFO & common_attr) &&
480            (ctx->f_attr.fa_valid_mask & FA_FINDERINFO_VALID)) {
481
482           bcopy(ctx->f_attr.fa_finder_info, &np->finfo,
483                 sizeof(u_int8_t) * 32);
484           nanouptime(&ts);
485           np->finfo_cache = ts.tv_sec;
486        }
487
488        /* Do we have updated Max Access? */
489        if ((max_access_from_cache == 0) &&
490            (ATTR_CMN_USERACCESS & common_attr) &&
491            (ctx->f_attr.fa_valid_mask & FA_MAX_ACCESS_VALID)) {
492
493            np->maxAccessRights = ctx->f_attr.fa_max_access;
494            np->maxAccessRightChTime = ctx->f_attr.fa_chtime;
495        }
496    }
497
498    /*
499     * Error Handling. At this point, we should have valid information and if
500     * we do not, then some earlier error must have occurred, so fill in with
501     * default values.
502     */
503    if ((need_rsrc_fork) && !(ctx->f_attr.fa_valid_mask & FA_RSRC_FORK_VALID)) {
504        /* Assume resource fork lengths of zero */
505        ctx->f_attr.fa_valid_mask |= FA_RSRC_FORK_VALID;
506        ctx->f_attr.fa_rsrc_size = 0;
507        ctx->f_attr.fa_rsrc_alloc = 0;
508    }
509
510    if ((ATTR_CMN_FNDRINFO & common_attr) &&
511        !(ctx->f_attr.fa_valid_mask & FA_FINDERINFO_VALID)) {
512        /* Assume zero Finder Info */
513        ctx->f_attr.fa_valid_mask |= FA_FINDERINFO_VALID;
514        bcopy(&zero_finfo, ctx->f_attr.fa_finder_info, sizeof(u_int8_t) * 32);
515    }
516
517    if ((ATTR_CMN_USERACCESS & common_attr) &&
518        !(ctx->f_attr.fa_valid_mask & FA_MAX_ACCESS_VALID)) {
519        /* Assume full access */
520        ctx->f_attr.fa_valid_mask |= FA_MAX_ACCESS_VALID;
521        ctx->f_attr.fa_max_access = SA_RIGHT_FILE_ALL_ACCESS | STD_RIGHT_ALL_ACCESS;
522    }
523
524    /*
525     * At this point we should have all the meta data we need to fill out the
526     * return attribute block.
527     */
528	if (attrlistp->commonattr) {
529		packcommonattr(abp, smp, ctx, context);
530    }
531
532	if (attrlistp->dirattr && is_dir) {
533		packdirattr(abp, vp, ctx);
534    }
535
536	if (attrlistp->fileattr && !is_dir) {
537		packfileattr(abp, smp, ctx, context);
538    }
539}
540
541static void
542packcommonattr(struct attrblock *abp,
543               struct smbmount *smp,
544               struct smbfs_fctx *ctx,
545               struct vfs_context *context)
546{
547	attrgroup_t attr = abp->ab_attrlist->commonattr;
548	struct mount *mp = smp->sm_mp;
549	void *attrbufptr = *abp->ab_attrbufpp;
550	void *varbufptr = *abp->ab_varbufpp;
551	boolean_t is_64_bit = proc_is64bit(vfs_context_proc(context));
552	uid_t cuid = 1;
553	int isroot = 0;
554	struct timespec temp_time;
555    uid_t uid = KAUTH_UID_NONE;
556    gid_t gid = KAUTH_GID_NONE;
557    mode_t mode = 0;
558    uint32_t flags = 0;
559    uint32_t cmn_user_rights = 0;
560    uint32_t ino;
561
562    /* Calculate uid, gid and mode from Query Dir results */
563    if (ctx->f_attr.fa_attr & SMB_EFA_DIRECTORY) {
564        flags |= SMBFS_GET_UGM_IS_DIR;
565    }
566
567    /* Get the uid, gid and mode */
568    smb_get_uid_gid_mode(ctx->f_share, smp,
569                         &ctx->f_attr, flags,
570                         &uid, &gid, &mode);
571
572    /* This is used later in this function... */
573    if (attr & (ATTR_CMN_OWNERID | ATTR_CMN_GRPID)) {
574		cuid = kauth_cred_getuid(vfs_context_ucred(context));
575		isroot = cuid == 0;
576	}
577
578	if (ATTR_CMN_NAME & attr) {
579        packnameattr(abp, ctx, (const u_int8_t *) ctx->f_LocalName,
580                     ctx->f_LocalNameLen);
581
582		attrbufptr = *abp->ab_attrbufpp;
583		varbufptr = *abp->ab_varbufpp;
584	}
585
586	if (ATTR_CMN_DEVID & attr) {
587        /* Copy AFP Client behavior */
588		*((dev_t *)attrbufptr) = vfs_statfs(mp)->f_fsid.val[0];
589		attrbufptr = ((dev_t *)attrbufptr) + 1;
590	}
591
592	if (ATTR_CMN_FSID & attr) {
593        /* Copy AFP Client behavior */
594        *((fsid_t *)attrbufptr) = vfs_statfs(mp)->f_fsid;
595		attrbufptr = ((fsid_t *)attrbufptr) + 1;
596	}
597
598	if (ATTR_CMN_OBJTYPE & attr) {
599        /*
600         * Because of the Steve/Conrad Symlinks we can never be completely
601         * sure that we have the correct vnode type if its a file. Since we
602         * don't support Steve/Conrad Symlinks with Darwin we can always count
603         * on the vtype being correct. For directories we always know the
604         * correct information.
605         */
606        *((fsobj_type_t *)attrbufptr) = ctx->f_attr.fa_vtype;
607
608		attrbufptr = ((fsobj_type_t *)attrbufptr) + 1;
609	}
610
611	if (ATTR_CMN_OBJTAG & attr) {
612		*((fsobj_tag_t *)attrbufptr) = VT_CIFS;
613		attrbufptr = ((fsobj_tag_t *)attrbufptr) + 1;
614	}
615
616	/*
617	 * Exporting file IDs from HFS Plus:
618	 *
619	 * For "normal" files the c_fileid is the same value as the
620	 * c_cnid.  But for hard link files, they are different - the
621	 * c_cnid belongs to the active directory entry (ie the link)
622	 * and the c_fileid is for the actual inode (ie the data file).
623	 *
624	 * The stat call (getattr) will always return the c_fileid
625	 * and Carbon APIs, which are hardlink-ignorant, will always
626	 * receive the c_cnid (from getattrlist).
627	 */
628	if (ATTR_CMN_OBJID & attr) {
629        /*
630         * VOL_CAP_FMT_64BIT_OBJECT_IDS is set so this value is undefined
631         */
632        ino = (uint32_t) smb2fs_smb_file_id_get(smp, ctx->f_attr.fa_ino,
633                                                ctx->f_LocalName);
634
635        ((fsobj_id_t *)attrbufptr)->fid_objno = ino;
636		((fsobj_id_t *)attrbufptr)->fid_generation = 0;
637		attrbufptr = ((fsobj_id_t *)attrbufptr) + 1;
638	}
639
640	if (ATTR_CMN_OBJPERMANENTID & attr) {
641        /*
642         * VOL_CAP_FMT_64BIT_OBJECT_IDS is set so this value is undefined
643         */
644        ino = (uint32_t) smb2fs_smb_file_id_get(smp, ctx->f_attr.fa_ino,
645                                                ctx->f_LocalName);
646
647        ((fsobj_id_t *)attrbufptr)->fid_objno = ino;
648		((fsobj_id_t *)attrbufptr)->fid_generation = 0;
649		attrbufptr = ((fsobj_id_t *)attrbufptr) + 1;
650	}
651
652	if (ATTR_CMN_PAROBJID & attr) {
653        /*
654         * VOL_CAP_FMT_64BIT_OBJECT_IDS is set so this value is undefined
655         */
656        ino = (uint32_t) smb2fs_smb_file_id_get(smp, ctx->f_dnp->n_ino,
657                                                ctx->f_dnp->n_name);
658
659        ((fsobj_id_t *)attrbufptr)->fid_objno = ino;
660		((fsobj_id_t *)attrbufptr)->fid_generation = 0;
661		attrbufptr = ((fsobj_id_t *)attrbufptr) + 1;
662	}
663
664	if (ATTR_CMN_SCRIPT & attr) {
665        /* %%% TO DO: What should this be??? */
666		*((text_encoding_t *)attrbufptr) = 0;  /* kTextEncodingMacRoman */
667		attrbufptr = ((text_encoding_t *)attrbufptr) + 1;
668	}
669
670	if (ATTR_CMN_CRTIME & attr) {
671        temp_time = ctx->f_attr.fa_crtime;
672
673	    if (is_64_bit) {
674            ((struct user64_timespec *)attrbufptr)->tv_sec = temp_time.tv_sec;
675            ((struct user64_timespec *)attrbufptr)->tv_nsec = temp_time.tv_nsec;
676			attrbufptr = ((struct user64_timespec *)attrbufptr) + 1;
677	    }
678	    else {
679            /* Seems like getattr just slams in uint64_t into uint32_t fields */
680            ((struct user32_timespec *)attrbufptr)->tv_sec = (uint32_t) temp_time.tv_sec;
681            ((struct user32_timespec *)attrbufptr)->tv_nsec = (uint32_t) temp_time.tv_nsec;
682			attrbufptr = ((struct user32_timespec *)attrbufptr) + 1;
683	    }
684	}
685
686	if (ATTR_CMN_MODTIME & attr) {
687        temp_time = ctx->f_attr.fa_mtime;
688
689	    if (is_64_bit) {
690             ((struct user64_timespec *)attrbufptr)->tv_sec = temp_time.tv_sec;
691             ((struct user64_timespec *)attrbufptr)->tv_nsec = temp_time.tv_nsec;
692			 attrbufptr = ((struct user64_timespec *)attrbufptr) + 1;
693	    }
694	    else {
695            /* Seems like getattr just slams in uint64_t into uint32_t fields */
696            ((struct user32_timespec *)attrbufptr)->tv_sec = (uint32_t) temp_time.tv_sec;
697            ((struct user32_timespec *)attrbufptr)->tv_nsec = (uint32_t) temp_time.tv_nsec;
698			attrbufptr = ((struct user32_timespec *)attrbufptr) + 1;
699	    }
700	}
701
702	if (ATTR_CMN_CHGTIME & attr) {
703        temp_time = ctx->f_attr.fa_chtime;
704
705	    if (is_64_bit) {
706            ((struct user64_timespec *)attrbufptr)->tv_sec = temp_time.tv_sec;
707            ((struct user64_timespec *)attrbufptr)->tv_nsec = temp_time.tv_nsec;
708			attrbufptr = ((struct user64_timespec *)attrbufptr) + 1;
709	    }
710	    else {
711            /* Seems like getattr just slams in uint64_t into uint32_t fields */
712            ((struct user32_timespec *)attrbufptr)->tv_sec = (uint32_t) temp_time.tv_sec;
713            ((struct user32_timespec *)attrbufptr)->tv_nsec = (uint32_t) temp_time.tv_nsec;
714			attrbufptr = ((struct user32_timespec *)attrbufptr) + 1;
715	    }
716	}
717
718	if (ATTR_CMN_ACCTIME & attr) {
719        temp_time = ctx->f_attr.fa_atime;
720
721	    if (is_64_bit) {
722            ((struct user64_timespec *)attrbufptr)->tv_sec = temp_time.tv_sec;
723            ((struct user64_timespec *)attrbufptr)->tv_nsec = temp_time.tv_nsec;
724			attrbufptr = ((struct user64_timespec *)attrbufptr) + 1;
725	    }
726	    else {
727            /* Seems like getattr just slams in uint64_t into uint32_t fields */
728            ((struct user32_timespec *)attrbufptr)->tv_sec = (uint32_t) temp_time.tv_sec;
729            ((struct user32_timespec *)attrbufptr)->tv_nsec = (uint32_t) temp_time.tv_nsec;
730			attrbufptr = ((struct user32_timespec *)attrbufptr) + 1;
731	    }
732	}
733
734	if (ATTR_CMN_BKUPTIME & attr) {
735        /* Backup time not supported so return 0 */
736	    if (is_64_bit) {
737            ((struct user64_timespec *)attrbufptr)->tv_sec = 0;
738            ((struct user64_timespec *)attrbufptr)->tv_nsec = 0;
739			attrbufptr = ((struct user64_timespec *)attrbufptr) + 1;
740	    }
741	    else {
742            ((struct user32_timespec *)attrbufptr)->tv_sec = 0;
743            ((struct user32_timespec *)attrbufptr)->tv_nsec = 0;
744			attrbufptr = ((struct user32_timespec *)attrbufptr) + 1;
745	    }
746	}
747
748	if (ATTR_CMN_FNDRINFO & attr) {
749        DBG_ASSERT(ctx->f_attr.fa_valid_mask & FA_FINDERINFO_VALID);
750        bcopy(ctx->f_attr.fa_finder_info, attrbufptr, sizeof(u_int8_t) * 32);
751		attrbufptr = (char *)attrbufptr + sizeof(u_int8_t) * 32;
752	}
753
754	if (ATTR_CMN_OWNERID & attr) {
755        uid_t nuid;
756
757        if (SMBV_HAS_GUEST_ACCESS(SSTOVC(ctx->f_share))) {
758            nuid = UNKNOWNUID;
759		}
760        else {
761			/*
762			 * For servers that support the UNIX extensions we know the uid/gid.
763			 * For server that don't support ACLs then the node uid/gid will be
764			 * set to the mounted user's uid/gid. For all other servers we need
765			 * to get the ACL and translate the SID to a uid or gid. The uid/gid
766			 * really is for display purpose only and means nothing to us. We will
767			 * set the nodes ids if we get a request for the ACL, but otherwise
768			 * we leave them unset for performance reasons.
769			 */
770			if (ctx->f_attr.fa_uid == KAUTH_UID_NONE) {
771                nuid = smp->sm_args.uid;
772            }
773			else {
774                nuid = uid;
775            }
776		}
777
778		if (!isroot) {
779			if (((unsigned int)vfs_flags(smp->sm_mp)) & MNT_UNKNOWNPERMISSIONS)
780				nuid = cuid;
781			else if (nuid == UNKNOWNUID)
782				nuid = cuid;
783		}
784
785		*((uid_t *)attrbufptr) = nuid;
786		attrbufptr = ((uid_t *)attrbufptr) + 1;
787	}
788
789	if (ATTR_CMN_GRPID & attr) {
790		gid_t ngid;
791
792        if (SMBV_HAS_GUEST_ACCESS(SSTOVC(ctx->f_share))) {
793            ngid = UNKNOWNGID;
794		}
795        else {
796			/*
797			 * For servers that support the UNIX extensions we know the uid/gid.
798			 * For server that don't support ACLs then the node uid/gid will be
799			 * set to the mounted user's uid/gid. For all other servers we need
800			 * to get the ACL and translate the SID to a uid or gid. The uid/gid
801			 * really is for display purpose only and means nothing to us. We will
802			 * set the nodes ids if we get a request for the ACL, but otherwise
803			 * we leave them unset for performance reasons.
804			 */
805			if (ctx->f_attr.fa_gid == KAUTH_GID_NONE) {
806                ngid = smp->sm_args.gid;
807            }
808			else {
809                ngid = gid;
810            }
811		}
812
813		if (!isroot) {
814			gid_t cgid = kauth_cred_getgid(vfs_context_ucred(context));
815			if (((unsigned int)vfs_flags(smp->sm_mp)) & MNT_UNKNOWNPERMISSIONS)
816				ngid = cgid;
817			else if (ngid == UNKNOWNUID)
818				ngid = cgid;
819		}
820
821		*((gid_t *)attrbufptr) = ngid;
822		attrbufptr = ((gid_t *)attrbufptr) + 1;
823	}
824
825	if (ATTR_CMN_ACCESSMASK & attr) {
826        if (ctx->f_attr.fa_vtype == VDIR) {
827            *((u_int32_t *)attrbufptr) = (S_IFDIR | mode);
828        }
829
830        if (ctx->f_attr.fa_vtype == VREG) {
831            *((u_int32_t *)attrbufptr) = (S_IFREG | mode);
832        }
833
834        if (ctx->f_attr.fa_vtype == VLNK) {
835            *((u_int32_t *)attrbufptr) = (S_IFLNK | mode);
836        }
837
838		attrbufptr = ((u_int32_t *)attrbufptr) + 1;
839	}
840
841	if (ATTR_CMN_FLAGS & attr) {
842        uint32_t va_flags = 0;
843
844        if (ctx->f_attr.fa_attr & SMB_EFA_HIDDEN) {
845            /*
846             * Dont have to special case whether root vnode is hidden or not.
847             * root volume doesn't show up in a readdirattr, I think?
848             */
849            va_flags |= UF_HIDDEN;
850        }
851
852        if (ctx->f_attr.fa_attr & SMB_EFA_ARCHIVE) {
853            va_flags |= SF_ARCHIVED;
854        }
855
856        if (ctx->f_attr.fa_attr & SMB_EFA_RDONLY) {
857            va_flags |= UF_IMMUTABLE;
858        }
859
860		*((u_int32_t *)attrbufptr) = va_flags;
861		attrbufptr = ((u_int32_t *)attrbufptr) + 1;
862	}
863
864	if (ATTR_CMN_USERACCESS & attr) {
865        /*
866         * The effective permissions for the current user which we derive
867         * from the max access
868         */
869        DBG_ASSERT(ctx->f_attr.fa_valid_mask & FA_MAX_ACCESS_VALID);
870
871        if (ctx->f_attr.fa_max_access & SMB2_FILE_READ_DATA) {
872            cmn_user_rights |= R_OK;
873        }
874
875        if (ctx->f_attr.fa_max_access & SMB2_FILE_WRITE_DATA) {
876            cmn_user_rights |= W_OK;
877        }
878
879        if (ctx->f_attr.fa_max_access & SMB2_FILE_EXECUTE) {
880            cmn_user_rights |= X_OK;
881        }
882
883		*((u_int32_t *)attrbufptr) = cmn_user_rights;
884		attrbufptr = ((u_int32_t *)attrbufptr) + 1;
885	}
886
887	if (ATTR_CMN_FILEID & attr) {
888        *((u_int64_t *)attrbufptr) = smb2fs_smb_file_id_get(smp,
889                                                            ctx->f_attr.fa_ino,
890                                                            ctx->f_LocalName);
891		attrbufptr = ((u_int64_t *)attrbufptr) + 1;
892	}
893
894	if (ATTR_CMN_PARENTID & attr) {
895        *((u_int64_t *)attrbufptr) = smb2fs_smb_file_id_get(smp,
896                                                            ctx->f_dnp->n_ino,
897                                                            ctx->f_dnp->n_name);
898		attrbufptr = ((u_int64_t *)attrbufptr) + 1;
899	}
900
901	*abp->ab_attrbufpp = attrbufptr;
902	*abp->ab_varbufpp = varbufptr;
903}
904
905static void
906packdirattr(struct attrblock *abp,
907            struct vnode *vp,
908            struct smbfs_fctx *ctx)
909{
910	attrgroup_t attr = abp->ab_attrlist->dirattr;
911	void *attrbufptr = *abp->ab_attrbufpp;
912	u_int32_t entries;
913    uint32_t mnt_status;
914
915	/*
916	 * The DIR_LINKCOUNT is the count of real directory hard links.
917	 * (i.e. its not the sum of the implied "." and ".." references
918	 *  typically used in stat's st_nlink field)
919	 */
920	if (ATTR_DIR_LINKCOUNT & attr) {
921        /* There ARE no hard links, at least not yet... */
922		*((u_int32_t *)attrbufptr) = 1;
923		attrbufptr = ((u_int32_t *)attrbufptr) + 1;
924	}
925
926	if (ATTR_DIR_ENTRYCOUNT & attr) {
927        /* Apparently 0 is a fine answer to return for a filesystem */
928		entries = 0;
929
930		*((u_int32_t *)attrbufptr) = entries;
931		attrbufptr = ((u_int32_t *)attrbufptr) + 1;
932	}
933
934	if (ATTR_DIR_MOUNTSTATUS & attr) {
935        mnt_status = 0;
936
937        /* if its a DFS reparse point, then its a mnt trigger */
938        if ((ctx->f_attr.fa_attr & SMB_EFA_REPARSE_POINT) &&
939            (ctx->f_attr.fa_reparse_tag == IO_REPARSE_TAG_DFS)) {
940            mnt_status |= DIR_MNTSTATUS_TRIGGER;
941        }
942
943        /* If we have a vnode, check to see if its already mounted on */
944        if ((vp != NULL) && vnode_mountedhere(vp)) {
945            mnt_status |= DIR_MNTSTATUS_MNTPOINT;
946        }
947
948        *((u_int32_t *)attrbufptr) = mnt_status;
949		attrbufptr = ((u_int32_t *)attrbufptr) + 1;
950	}
951
952	*abp->ab_attrbufpp = attrbufptr;
953}
954
955static void
956packfileattr(struct attrblock *abp,
957             struct smbmount *smp,
958             struct smbfs_fctx *ctx,
959             struct vfs_context *context)
960{
961#pragma unused(context)
962	attrgroup_t attr = abp->ab_attrlist->fileattr;
963	void *attrbufptr = *abp->ab_attrbufpp;
964	void *varbufptr = *abp->ab_varbufpp;
965    uint64_t data_fork_size, data_fork_alloc;
966
967    /* Use values from ctx */
968    data_fork_size = ctx->f_attr.fa_size;
969    data_fork_alloc = ctx->f_attr.fa_data_alloc;
970
971    if (ATTR_FILE_LINKCOUNT & attr) {
972        /* There ARE no hard links, at least not yet... */
973		*((u_int32_t *)attrbufptr) = 1;
974		attrbufptr = ((u_int32_t *)attrbufptr) + 1;
975	}
976
977	if (ATTR_FILE_TOTALSIZE & attr) {
978        /* Return same value as smbfs_vnop_getattr */
979        DBG_ASSERT(ctx->f_attr.fa_valid_mask & FA_RSRC_FORK_VALID);
980
981		*((off_t *)attrbufptr) = data_fork_size + ctx->f_attr.fa_rsrc_size;
982		attrbufptr = ((off_t *)attrbufptr) + 1;
983	}
984
985	if (ATTR_FILE_ALLOCSIZE & attr) {
986        DBG_ASSERT(ctx->f_attr.fa_valid_mask & FA_RSRC_FORK_VALID);
987
988        /* Should already be rounded up */
989		*((off_t *)attrbufptr) = data_fork_alloc + ctx->f_attr.fa_rsrc_alloc;
990		attrbufptr = ((off_t *)attrbufptr) + 1;
991	}
992
993	if (ATTR_FILE_IOBLOCKSIZE & attr) {
994		*((u_int32_t *)attrbufptr) = smp->sm_statfsbuf.f_bsize;
995		attrbufptr = ((u_int32_t *)attrbufptr) + 1;
996	}
997
998	if (ATTR_FILE_CLUMPSIZE & attr) {
999        /* This attribute is obsolete so return 0 */
1000		*((u_int32_t *)attrbufptr) = 0;
1001		attrbufptr = ((u_int32_t *)attrbufptr) + 1;
1002	}
1003
1004	if (ATTR_FILE_DEVTYPE & attr) {
1005        /* Copy AFP Client behavior */
1006        *((u_int32_t *)attrbufptr) = 0;
1007		attrbufptr = ((u_int32_t *)attrbufptr) + 1;
1008	}
1009
1010	if (ATTR_FILE_DATALENGTH & attr) {
1011		*((off_t *)attrbufptr) = data_fork_size;
1012		attrbufptr = ((off_t *)attrbufptr) + 1;
1013	}
1014
1015    if (ATTR_FILE_DATAALLOCSIZE & attr) {
1016        *((off_t *)attrbufptr) = data_fork_alloc;
1017        attrbufptr = ((off_t *)attrbufptr) + 1;
1018    }
1019
1020    if (ATTR_FILE_RSRCLENGTH & attr) {
1021        DBG_ASSERT(ctx->f_attr.fa_valid_mask & FA_RSRC_FORK_VALID);
1022
1023        *((off_t *)attrbufptr) = ctx->f_attr.fa_rsrc_size;
1024        attrbufptr = ((off_t *)attrbufptr) + 1;
1025    }
1026
1027    if (ATTR_FILE_RSRCALLOCSIZE & attr) {
1028        DBG_ASSERT(ctx->f_attr.fa_valid_mask & FA_RSRC_FORK_VALID);
1029
1030        *((off_t *)attrbufptr) = ctx->f_attr.fa_rsrc_alloc;
1031        attrbufptr = ((off_t *)attrbufptr) + 1;
1032    }
1033
1034	*abp->ab_attrbufpp = attrbufptr;
1035	*abp->ab_varbufpp = varbufptr;
1036}
1037
1038static void
1039packnameattr(struct attrblock *abp, struct smbfs_fctx *ctx,
1040             const u_int8_t *name, size_t namelen)
1041{
1042	void *varbufptr;
1043	struct attrreference * attr_refptr;
1044	char *mpname;
1045	size_t mpnamelen;
1046	u_int32_t attrlength;
1047	u_int8_t empty = 0;
1048	struct smbmount *smp;
1049
1050	/* A cnode's name may be incorrect for the root of a mounted
1051	 * filesystem (it can be mounted on a different directory name
1052	 * than the name of the volume, such as "blah-1").  So for the
1053	 * root directory, it's best to return the last element of the
1054	 location where the volume's mounted:
1055	 */
1056
1057	smp = ctx->f_share->ss_mount;
1058    if ((ctx->f_attr.fa_ino == smp->sm_root_ino) &&
1059	    (mpname = mountpointname(vnode_mount(ctx->f_dnp->n_vnode)))) {
1060		mpnamelen = strlen(mpname);
1061
1062		/* Trim off any trailing slashes: */
1063		while ((mpnamelen > 0) && (mpname[mpnamelen-1] == '/'))
1064			--mpnamelen;
1065
1066		/* If there's anything left, use it instead of the volume's name */
1067		if (mpnamelen > 0) {
1068			name = (u_int8_t *)mpname;
1069			namelen = mpnamelen;
1070		}
1071	}
1072	if (name == NULL) {
1073		name = &empty;
1074		namelen = 0;
1075	}
1076
1077	varbufptr = *abp->ab_varbufpp;
1078	attr_refptr = (struct attrreference *)(*abp->ab_attrbufpp);
1079
1080	attrlength = (uint32_t) (namelen + 1);
1081	attr_refptr->attr_dataoffset = (uint32_t) ((char *)varbufptr - (char *)attr_refptr);
1082	attr_refptr->attr_length = attrlength;
1083	(void) strncpy((char *)varbufptr, (const char *) name, attrlength);
1084	/*
1085	 * Advance beyond the space just allocated and
1086	 * round up to the next 4-byte boundary:
1087	 */
1088	varbufptr = ((char *)varbufptr) + attrlength + ((4 - (attrlength & 3)) & 3);
1089	++attr_refptr;
1090
1091	*abp->ab_attrbufpp = attr_refptr;
1092	*abp->ab_varbufpp = varbufptr;
1093}
1094
1095/*
1096 * readdirattr operation will return attributes for the items in the
1097 * directory specified.
1098 *
1099 * It does not do . and .. entries. The problem is if you are at the root of the
1100 * smbfs directory and go to .. you could be crossing a mountpoint into a
1101 * different (ufs) file system. The attributes that apply for it may not
1102 * apply for the file system you are doing the readdirattr on. To make life
1103 * simpler, this call will only return entries in its directory, hfs like.
1104 */
1105int
1106smbfs_vnop_readdirattr(ap)
1107struct vnop_readdirattr_args /* {
1108                              struct vnode *a_vp;
1109                              struct attrlist *a_alist;
1110                              struct uio *a_uio;
1111                              u_long a_maxcount;
1112                              u_long a_options;
1113                              u_long *a_newstate;
1114                              int *a_eofflag;
1115                              u_long *a_actualcount;
1116                              vfs_context_t a_context;
1117                              } */ *ap;
1118{
1119	struct vnode *vp = NULL;
1120	struct vnode *dvp = ap->a_vp;
1121	struct attrlist *alist = ap->a_alist;
1122	uio_t uio = ap->a_uio;
1123	int maxcount = ap->a_maxcount;
1124	u_int32_t fixedblocksize;
1125	u_int32_t maxattrblocksize;
1126	u_int32_t currattrbufsize;
1127	void *attrbufptr = NULL;
1128	void *attrptr;
1129	void *varptr;
1130	struct attrblock attrblk;
1131    vfs_context_t context = ap->a_context;
1132	struct smbnode *dnp = NULL;
1133	struct smbfs_fctx *ctx;
1134	off_t offset;
1135	int error = 0;
1136    struct smb_share *share = NULL;
1137    struct smbmount *smp = NULL;
1138
1139	*(ap->a_actualcount) = 0;
1140	*(ap->a_eofflag) = 0;
1141
1142	/* Check for invalid options and buffer space. */
1143	if (((ap->a_options & ~(FSOPT_NOINMEMUPDATE | FSOPT_NOFOLLOW)) != 0) ||
1144	    (uio_resid(uio) <= 0) || (uio_iovcnt(uio) > 1) || (maxcount <= 0)) {
1145        SMBDEBUG("Invalid options or buf size\n");
1146		return (EINVAL);
1147	}
1148
1149	/*
1150	 * Reject requests for unsupported attributes.
1151	 */
1152	if ((alist->bitmapcount != ATTR_BIT_MAP_COUNT) ||
1153	    (alist->commonattr & ~SMBFS_ATTR_CMN_VALID) ||
1154	    (alist->volattr  != 0) ||
1155	    (alist->dirattr & ~SMBFS_ATTR_DIR_VALID) ||
1156	    (alist->fileattr & ~SMBFS_ATTR_FILE_VALID) ||
1157	    (alist->forkattr != 0)) {
1158        SMBDEBUG("Unsupported attributes\n");
1159		return (EINVAL);
1160	}
1161
1162    /*
1163     * Lock parent dir that we are enumerating
1164     */
1165	if ((error = smbnode_lock(VTOSMB(dvp), SMBFS_EXCLUSIVE_LOCK)))
1166		return (error);
1167	VTOSMB(dvp)->n_lastvop = smbfs_vnop_readdirattr;
1168
1169	dnp = VTOSMB(dvp);
1170	smp = VTOSMBFS(dvp);
1171
1172	/*
1173     * Do we need to start or restart the directory listing
1174     *
1175     * The uio_offset is actually just used to store whatever we want.  In HFS,
1176     * they store an index and a dir tag. For SMB, we will store just the offset
1177     */
1178	offset = uio_offset(uio);
1179
1180    /* Get Share reference */
1181	share = smb_get_share_with_reference(VTOSMBFS(dvp));
1182	if (!dnp->d_fctx || (dnp->d_fctx->f_share != share) || (offset == 0) ||
1183		(offset != dnp->d_offset)) {
1184		smbfs_closedirlookup(dnp, context);
1185		error = smbfs_smb_findopen(share, dnp, "*", 1, &dnp->d_fctx, TRUE,
1186                                   context);
1187	}
1188
1189	/*
1190	 * The directory fctx keeps a reference on the share so we can release our
1191	 * reference on the share now.
1192	 */
1193	smb_share_rele(share, context);
1194
1195	if (error) {
1196		SMBERROR("Can't open search for %s, error = %d", dnp->n_name, error);
1197		goto done;
1198	}
1199	ctx = dnp->d_fctx;
1200
1201	/*
1202     * Get a buffer to hold packed attributes.
1203     */
1204	fixedblocksize = (sizeof(u_int32_t) + attrblksize(alist)); /* 4 bytes for length */
1205	maxattrblocksize = fixedblocksize;
1206	if (alist->commonattr & ATTR_CMN_NAME)
1207		maxattrblocksize += (share->ss_maxfilenamelen * 3) + 1;
1208
1209	MALLOC(attrbufptr, void *, maxattrblocksize, M_TEMP, M_WAITOK);
1210	if (attrbufptr == NULL) {
1211		error = ENOMEM;
1212		goto done;
1213	}
1214	attrptr = attrbufptr;
1215	varptr = (char *)attrbufptr + fixedblocksize;  /* Point to variable-length storage */
1216
1217    /*
1218	 * They are continuing from some point ahead of us in the buffer. Skip all
1219	 * entries until we reach their point in the buffer.
1220	 */
1221	while (dnp->d_offset < offset) {
1222		error = smbfs_findnext(ctx, context);
1223		if (error) {
1224			smbfs_closedirlookup(dnp, context);
1225			goto done;
1226		}
1227		dnp->d_offset++;
1228	}
1229
1230	/* Loop until we end the search or we don't have enough room for the max element */
1231    while (uio_resid(uio)) {
1232        /* Get one entry out of the buffer and fill in ctx with its info */
1233		error = smbfs_findnext(ctx, context);
1234		if (error) {
1235			break;
1236        }
1237
1238        /*
1239         * <14430881> If file IDs are supported by this server, skip any
1240         * child that has the same id as the current parent that we are
1241         * enumerating. Seems like snapshot dirs have the same id as the parent
1242         * and that will cause us to deadlock when we find the vnode with same
1243         * id and then try to lock it again (deadlock on parent id).
1244         */
1245        if (SSTOVC(share)->vc_misc_flags & SMBV_HAS_FILEIDS) {
1246            if (ctx->f_attr.fa_ino == dnp->n_ino) {
1247                SMBDEBUG("Skipping <%s> as it has same ID as parent\n",
1248                         ctx->f_LocalName);
1249                continue;
1250            }
1251        }
1252
1253        /*
1254         * Check to see if vnode already exists. If so, then can pull the data
1255         * from the inode instead of having to poll the server for missing info
1256         */
1257        error = smbfs_nget(share, vnode_mount(dvp),
1258                           dvp, ctx->f_LocalName, ctx->f_LocalNameLen,
1259                           &ctx->f_attr, &vp,
1260                           0, SMBFS_NGET_LOOKUP_ONLY,
1261                           ap->a_context);
1262        /* dont care if we got an error or not, just whether vp == NULL or not */
1263        if ((error == 0) && (vp != NULL)) {
1264            /*
1265             * Enumerates alway return the correct case of the name.
1266             * Update the name and parent if needed.
1267             */
1268            smbfs_update_name_par(ctx->f_share, dvp, vp,
1269                                  &ctx->f_attr.fa_reqtime,
1270                                  ctx->f_LocalName, ctx->f_LocalNameLen);
1271        }
1272
1273		*((u_int32_t *)attrptr) = 0;
1274		attrptr = ((u_int32_t *)attrptr) + 1;
1275		attrblk.ab_attrlist = alist;
1276		attrblk.ab_attrbufpp = &attrptr;
1277		attrblk.ab_varbufpp = &varptr;
1278		attrblk.ab_flags = 0;
1279		attrblk.ab_blocksize = maxattrblocksize;
1280		attrblk.ab_context = ap->a_context;
1281
1282		/*
1283         * Pack catalog entries into attribute buffer.
1284         */
1285		packattrblk(&attrblk, smp, vp, ctx, context);
1286		currattrbufsize = (uint32_t) ((char *)varptr - (char *)attrbufptr);
1287
1288        /* Done with the vp */
1289        if (vp != NULL) {
1290            smbnode_unlock(VTOSMB(vp));
1291            vnode_put(vp);
1292        }
1293
1294        /*
1295         * Make sure there's enough buffer space remaining.
1296         */
1297		// LP64todo - fix this!
1298		if (uio_resid(uio) < 0 || (currattrbufsize > (u_int32_t)uio_resid(uio))) {
1299			break;
1300		}
1301        else {
1302			*((u_int32_t *)attrbufptr) = currattrbufsize;
1303			error = uiomove((caddr_t)attrbufptr, currattrbufsize, uio);
1304			if (error) {
1305				break;
1306			}
1307
1308			attrptr = attrbufptr;
1309			/* Point to variable-length storage */
1310			varptr = (char *)attrbufptr + fixedblocksize;
1311
1312			*ap->a_actualcount += 1;
1313            dnp->d_offset++;
1314
1315			/* Termination checks */
1316			if ((--maxcount <= 0) ||
1317			    // LP64todo - fix this!
1318			    uio_resid(uio) < 0 ||
1319			    ((u_int32_t)uio_resid(uio) < (fixedblocksize + SMBFS_AVERAGE_NAME_SIZE))) {
1320				break;
1321			}
1322		}
1323    } /* while loop */
1324
1325 	if (error == ENOENT) {
1326		*(ap->a_eofflag) = TRUE;
1327		error = 0;
1328	}
1329
1330	if (error) {
1331		goto done;
1332	}
1333
1334done:
1335	/* Last offset into uio_offset. */
1336	uio_setoffset(uio, dnp->d_offset);
1337
1338    *ap->a_newstate = dnp->d_changecnt;
1339
1340	if (attrbufptr) {
1341		FREE(attrbufptr, M_TEMP);
1342    }
1343
1344	smbnode_unlock(VTOSMB(dvp));
1345
1346	return (error);
1347}
1348
1349