Index: sys/fs/autofs/autofs.h =================================================================== --- sys/fs/autofs/autofs.h +++ sys/fs/autofs/autofs.h @@ -120,13 +120,6 @@ int sc_last_request_id; }; -/* - * Limits and constants - */ -#define AUTOFS_NAMELEN 24 -#define AUTOFS_FSNAMELEN 16 /* equal to MFSNAMELEN */ -#define AUTOFS_DELEN (8 + AUTOFS_NAMELEN) - int autofs_init(struct vfsconf *vfsp); int autofs_uninit(struct vfsconf *vfsp); int autofs_trigger(struct autofs_node *anp, const char *component, Index: sys/fs/autofs/autofs_vnops.c =================================================================== --- sys/fs/autofs/autofs_vnops.c +++ sys/fs/autofs/autofs_vnops.c @@ -339,23 +339,44 @@ return (error); } +static size_t +autofs_dirent_reclen(const char *name) +{ + size_t namlen, reclen; + + namlen = roundup2(strlen(name) + 1, 4); + KASSERT(namlen <= MAXNAMLEN, ("%zd > MAXNAMLEN", namlen)); + reclen = offsetof(struct dirent, d_name) + namlen; + + return (reclen); +} + static int -autofs_readdir_one(struct uio *uio, const char *name, int fileno) +autofs_readdir_one(struct uio *uio, const char *name, int fileno, + size_t *reclenp) { struct dirent dirent; - int error, i; + size_t namlen, reclen; + int error; + + namlen = roundup2(strlen(name) + 1, 4); + KASSERT(namlen <= MAXNAMLEN, ("%zd > MAXNAMLEN", namlen)); + reclen = offsetof(struct dirent, d_name) + namlen; + + if (uio->uio_resid < reclen) + return (EINVAL); memset(&dirent, 0, sizeof(dirent)); - dirent.d_type = DT_DIR; - dirent.d_reclen = AUTOFS_DELEN; dirent.d_fileno = fileno; - /* PFS_DELEN was picked to fit PFS_NAMLEN */ - for (i = 0; i < AUTOFS_NAMELEN - 1 && name[i] != '\0'; ++i) - dirent.d_name[i] = name[i]; - dirent.d_name[i] = 0; - dirent.d_namlen = i; + dirent.d_reclen = reclen; + dirent.d_type = DT_DIR; + dirent.d_namlen = namlen; + strlcpy(dirent.d_name, name, namlen); + + error = uiomove(&dirent, reclen, uio); + if (reclenp != NULL) + *reclenp = reclen; - error = uiomove(&dirent, AUTOFS_DELEN, uio); return (error); } @@ -366,8 +387,8 @@ struct autofs_mount *amp; struct autofs_node *anp, *child; struct uio *uio; - off_t offset; - int error, i, resid; + size_t reclen, reclens; + int error; vp = ap->a_vp; amp = VFSTOAUTOFS(vp->v_mount); @@ -390,69 +411,66 @@ } } - /* only allow reading entire entries */ - offset = uio->uio_offset; - resid = uio->uio_resid; - if (offset < 0 || offset % AUTOFS_DELEN != 0 || - (resid && resid < AUTOFS_DELEN)) + if (uio->uio_offset < 0) return (EINVAL); - if (resid == 0) - return (0); if (ap->a_eofflag != NULL) *ap->a_eofflag = TRUE; - if (offset == 0 && resid >= AUTOFS_DELEN) { - error = autofs_readdir_one(uio, ".", anp->an_fileno); + if (uio->uio_offset == 0) { + error = autofs_readdir_one(uio, ".", anp->an_fileno, NULL); if (error != 0) return (error); - offset += AUTOFS_DELEN; - resid -= AUTOFS_DELEN; } - if (offset == AUTOFS_DELEN && resid >= AUTOFS_DELEN) { + reclens = autofs_dirent_reclen("."); + if (uio->uio_offset <= reclens) { + if (uio->uio_offset != reclens) + return (EINVAL); if (anp->an_parent == NULL) { - /* - * XXX: Right? - */ - error = autofs_readdir_one(uio, "..", anp->an_fileno); + error = autofs_readdir_one(uio, "..", + anp->an_fileno, NULL); } else { error = autofs_readdir_one(uio, "..", - anp->an_parent->an_fileno); + anp->an_parent->an_fileno, NULL); } if (error != 0) return (error); - offset += AUTOFS_DELEN; - resid -= AUTOFS_DELEN; } - i = 2; /* Account for "." and "..". */ + reclens += autofs_dirent_reclen(".."); + AUTOFS_SLOCK(amp); TAILQ_FOREACH(child, &anp->an_children, an_next) { - if (resid < AUTOFS_DELEN) { - if (ap->a_eofflag != NULL) - *ap->a_eofflag = 0; - break; - } - /* * Skip entries returned by previous call to getdents(). */ - i++; - if (i * AUTOFS_DELEN <= offset) + if (uio->uio_offset > reclens) { + reclens += autofs_dirent_reclen(child->an_name); continue; + } + + /* + * Prevent seeking into middle of a dirent. + */ + if (uio->uio_offset != reclens) + return (EINVAL); error = autofs_readdir_one(uio, child->an_name, - child->an_fileno); + child->an_fileno, &reclen); + reclens += reclen; if (error != 0) { + if (error == EINVAL && ap->a_eofflag != NULL) { + *ap->a_eofflag = 0; + break; + } + AUTOFS_SUNLOCK(amp); return (error); } - offset += AUTOFS_DELEN; - resid -= AUTOFS_DELEN; } - AUTOFS_SUNLOCK(amp); + return (0); }