1 /*        $NetBSD: msdosfs_vnops.c,v 1.113 2024/09/11 00:27:54 perseant Exp $   */
2 
3 /*-
4  * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
5  * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
6  * All rights reserved.
7  * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *        This product includes software developed by TooLs GmbH.
20  * 4. The name of TooLs GmbH may not be used to endorse or promote products
21  *    derived from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
29  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
31  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34 /*
35  * Written by Paul Popelka (paulp@uts.amdahl.com)
36  *
37  * You can do anything you want with this software, just don't say you wrote
38  * it, and don't remove this notice.
39  *
40  * This software is provided "as is".
41  *
42  * The author supplies this software to be publicly redistributed on the
43  * understanding that the author is not responsible for the correct
44  * functioning of this software in any circumstances and is not liable for
45  * any damages caused by this software.
46  *
47  * October 1992
48  */
49 
50 #include <sys/cdefs.h>
51 __KERNEL_RCSID(0, "$NetBSD: msdosfs_vnops.c,v 1.113 2024/09/11 00:27:54 perseant Exp $");
52 
53 #include <sys/param.h>
54 #include <sys/systm.h>
55 #include <sys/namei.h>
56 #include <sys/resourcevar.h>  /* defines plimit structure in proc struct */
57 #include <sys/kernel.h>
58 #include <sys/file.h>                   /* define FWRITE ... */
59 #include <sys/stat.h>
60 #include <sys/buf.h>
61 #include <sys/proc.h>
62 #include <sys/mount.h>
63 #include <sys/vnode.h>
64 #include <sys/signalvar.h>
65 #include <sys/malloc.h>
66 #include <sys/dirent.h>
67 #include <sys/lockf.h>
68 #include <sys/kauth.h>
69 
70 #include <miscfs/genfs/genfs.h>
71 #include <miscfs/specfs/specdev.h> /* XXX */      /* defines v_rdev */
72 
73 #include <uvm/uvm_extern.h>
74 
75 #include <fs/msdosfs/bpb.h>
76 #include <fs/msdosfs/direntry.h>
77 #include <fs/msdosfs/denode.h>
78 #include <fs/msdosfs/msdosfsmount.h>
79 #include <fs/msdosfs/fat.h>
80 
81 /*
82  * Some general notes:
83  *
84  * In the ufs filesystem the inodes, superblocks, and indirect blocks are
85  * read/written using the vnode for the filesystem. Blocks that represent
86  * the contents of a file are read/written using the vnode for the file
87  * (including directories when they are read/written as files). This
88  * presents problems for the dos filesystem because data that should be in
89  * an inode (if dos had them) resides in the directory itself.  Since we
90  * must update directory entries without the benefit of having the vnode
91  * for the directory we must use the vnode for the filesystem.  This means
92  * that when a directory is actually read/written (via read, write, or
93  * readdir, or seek) we must use the vnode for the filesystem instead of
94  * the vnode for the directory as would happen in ufs. This is to insure we
95  * retrieve the correct block from the buffer cache since the hash value is
96  * based upon the vnode address and the desired block number.
97  */
98 
99 /*
100  * Create a regular file. On entry the directory to contain the file being
101  * created is locked.  We must release before we return.
102  */
103 int
msdosfs_create(void * v)104 msdosfs_create(void *v)
105 {
106           struct vop_create_v3_args /* {
107                     struct vnode *a_dvp;
108                     struct vnode **a_vpp;
109                     struct componentname *a_cnp;
110                     struct vattr *a_vap;
111           } */ *ap = v;
112           struct componentname *cnp = ap->a_cnp;
113           struct denode ndirent;
114           struct denode *dep;
115           struct denode *pdep = VTODE(ap->a_dvp);
116           int error;
117 
118 #ifdef MSDOSFS_DEBUG
119           printf("msdosfs_create(cnp %p, vap %p\n", cnp, ap->a_vap);
120 #endif
121 
122           /*
123            * If this is the root directory and there is no space left we
124            * can't do anything.  This is because the root directory can not
125            * change size.
126            */
127           if (pdep->de_StartCluster == MSDOSFSROOT
128               && pdep->de_crap.mlr_fndoffset >= pdep->de_FileSize) {
129                     error = ENOSPC;
130                     goto bad;
131           }
132 
133           /*
134            * Create a directory entry for the file, then call createde() to
135            * have it installed. NOTE: DOS files are always executable.  We
136            * use the absence of the owner write bit to make the file
137            * readonly.
138            */
139           memset(&ndirent, 0, sizeof(ndirent));
140           if ((error = msdosfs_uniqdosname(pdep, cnp, ndirent.de_Name)) != 0)
141                     goto bad;
142 
143           ndirent.de_Attributes = (ap->a_vap->va_mode & S_IWUSR) ?
144                                         ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY;
145           ndirent.de_StartCluster = 0;
146           ndirent.de_FileSize = 0;
147           ndirent.de_dev = pdep->de_dev;
148           ndirent.de_devvp = pdep->de_devvp;
149           ndirent.de_pmp = pdep->de_pmp;
150           ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
151           DETIMES(&ndirent, NULL, NULL, NULL, pdep->de_pmp->pm_gmtoff);
152           if ((error = msdosfs_createde(&ndirent, pdep, &pdep->de_crap, &dep,
153               cnp)) != 0)
154                     goto bad;
155           *ap->a_vpp = DETOV(dep);
156           cache_enter(ap->a_dvp, *ap->a_vpp, cnp->cn_nameptr, cnp->cn_namelen,
157               cnp->cn_flags);
158           return (0);
159 
160 bad:
161           return (error);
162 }
163 
164 int
msdosfs_close(void * v)165 msdosfs_close(void *v)
166 {
167           struct vop_close_args /* {
168                     struct vnode *a_vp;
169                     int a_fflag;
170                     kauth_cred_t a_cred;
171           } */ *ap = v;
172           struct vnode *vp = ap->a_vp;
173           struct denode *dep = VTODE(vp);
174 
175           mutex_enter(vp->v_interlock);
176           if (vrefcnt(vp) > 1)
177                     DETIMES(dep, NULL, NULL, NULL, dep->de_pmp->pm_gmtoff);
178           mutex_exit(vp->v_interlock);
179           return (0);
180 }
181 
182 static int
msdosfs_check_possible(struct vnode * vp,struct denode * dep,accmode_t accmode)183 msdosfs_check_possible(struct vnode *vp, struct denode *dep, accmode_t accmode)
184 {
185 
186           /*
187            * Disallow write attempts on read-only file systems;
188            * unless the file is a socket, fifo, or a block or
189            * character device resident on the file system.
190            */
191           if (accmode & VWRITE) {
192                     switch (vp->v_type) {
193                     case VDIR:
194                     case VLNK:
195                     case VREG:
196                               if (vp->v_mount->mnt_flag & MNT_RDONLY)
197                                         return (EROFS);
198                     default:
199                               break;
200                     }
201           }
202 
203           return 0;
204 }
205 
206 static int
msdosfs_check_permitted(struct vnode * vp,struct denode * dep,accmode_t accmode,kauth_cred_t cred)207 msdosfs_check_permitted(struct vnode *vp, struct denode *dep, accmode_t accmode,
208     kauth_cred_t cred)
209 {
210           struct msdosfsmount *pmp = dep->de_pmp;
211           mode_t file_mode;
212 
213           if ((dep->de_Attributes & ATTR_READONLY) == 0)
214                     file_mode = S_IRWXU|S_IRWXG|S_IRWXO;
215           else
216                     file_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
217 
218           file_mode &= (vp->v_type == VDIR ? pmp->pm_dirmask : pmp->pm_mask);
219 
220           return kauth_authorize_vnode(cred, KAUTH_ACCESS_ACTION(accmode,
221               vp->v_type, file_mode), vp, NULL, genfs_can_access(vp, cred,
222               pmp->pm_uid, pmp->pm_gid, file_mode, NULL, accmode));
223 }
224 
225 int
msdosfs_access(void * v)226 msdosfs_access(void *v)
227 {
228           struct vop_access_args /* {
229                     struct vnode *a_vp;
230                     accmode_t a_accmode;
231                     kauth_cred_t a_cred;
232           } */ *ap = v;
233           struct vnode *vp = ap->a_vp;
234           struct denode *dep = VTODE(vp);
235           int error;
236 
237           error = msdosfs_check_possible(vp, dep, ap->a_accmode);
238           if (error)
239                     return error;
240 
241           error = msdosfs_check_permitted(vp, dep, ap->a_accmode, ap->a_cred);
242 
243           return error;
244 }
245 
246 int
msdosfs_getattr(void * v)247 msdosfs_getattr(void *v)
248 {
249           struct vop_getattr_args /* {
250                     struct vnode *a_vp;
251                     struct vattr *a_vap;
252                     kauth_cred_t a_cred;
253           } */ *ap = v;
254           struct denode *dep = VTODE(ap->a_vp);
255           struct msdosfsmount *pmp = dep->de_pmp;
256           struct vattr *vap = ap->a_vap;
257           mode_t mode;
258           u_long dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry);
259           ino_t fileid;
260 
261           DETIMES(dep, NULL, NULL, NULL, pmp->pm_gmtoff);
262           vap->va_fsid = dep->de_dev;
263           /*
264            * The following computation of the fileid must be the same as that
265            * used in msdosfs_readdir() to compute d_fileno. If not, pwd
266            * doesn't work.
267            */
268           if (dep->de_Attributes & ATTR_DIRECTORY) {
269                     fileid = cntobn(pmp, (ino_t)dep->de_StartCluster) * dirsperblk;
270                     if (dep->de_StartCluster == MSDOSFSROOT)
271                               fileid = 1;
272           } else {
273                     fileid = cntobn(pmp, (ino_t)dep->de_dirclust) * dirsperblk;
274                     if (dep->de_dirclust == MSDOSFSROOT)
275                               fileid = roottobn(pmp, 0) * dirsperblk;
276                     fileid += dep->de_diroffset / sizeof(struct direntry);
277           }
278           vap->va_fileid = fileid;
279           if ((dep->de_Attributes & ATTR_READONLY) == 0)
280                     mode = S_IRWXU|S_IRWXG|S_IRWXO;
281           else
282                     mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
283           vap->va_mode =
284               mode & (ap->a_vp->v_type == VDIR ? pmp->pm_dirmask : pmp->pm_mask);
285           vap->va_uid = pmp->pm_uid;
286           vap->va_gid = pmp->pm_gid;
287           vap->va_nlink = 1;
288           vap->va_rdev = 0;
289           vap->va_size = ap->a_vp->v_size;
290           msdosfs_dos2unixtime(dep->de_MDate, dep->de_MTime, 0, pmp->pm_gmtoff,
291               &vap->va_mtime);
292           if (dep->de_pmp->pm_flags & MSDOSFSMNT_LONGNAME) {
293                     msdosfs_dos2unixtime(dep->de_ADate, 0, 0, pmp->pm_gmtoff,
294                         &vap->va_atime);
295                     msdosfs_dos2unixtime(dep->de_CDate, dep->de_CTime, dep->de_CHun,
296                         pmp->pm_gmtoff, &vap->va_ctime);
297           } else {
298                     vap->va_atime = vap->va_mtime;
299                     vap->va_ctime = vap->va_mtime;
300           }
301           vap->va_flags = 0;
302           if ((dep->de_Attributes & ATTR_ARCHIVE) == 0) {
303                     vap->va_flags |= SF_ARCHIVED;
304                     vap->va_mode  |= S_ARCH1;
305           }
306           vap->va_gen = 0;
307           vap->va_blocksize = pmp->pm_bpcluster;
308           vap->va_bytes =
309               (dep->de_FileSize + pmp->pm_crbomask) & ~pmp->pm_crbomask;
310           vap->va_type = ap->a_vp->v_type;
311           return (0);
312 }
313 
314 int
msdosfs_setattr(void * v)315 msdosfs_setattr(void *v)
316 {
317           struct vop_setattr_args /* {
318                     struct vnode *a_vp;
319                     struct vattr *a_vap;
320                     kauth_cred_t a_cred;
321           } */ *ap = v;
322           int error = 0, de_changed = 0;
323           struct denode *dep = VTODE(ap->a_vp);
324           struct msdosfsmount *pmp = dep->de_pmp;
325           struct vnode *vp  = ap->a_vp;
326           struct vattr *vap = ap->a_vap;
327           kauth_cred_t cred = ap->a_cred;
328 
329 #ifdef MSDOSFS_DEBUG
330           printf("msdosfs_setattr(): vp %p, vap %p, cred %p\n",
331               ap->a_vp, vap, cred);
332 #endif
333           /*
334            * Note we silently ignore uid or gid changes.
335            */
336           if ((vap->va_type != VNON) || (vap->va_nlink != (nlink_t)VNOVAL) ||
337               (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
338               (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
339               (vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL) ||
340               (vap->va_uid != VNOVAL && vap->va_uid != pmp->pm_uid) ||
341               (vap->va_gid != VNOVAL && vap->va_gid != pmp->pm_gid)) {
342 #ifdef MSDOSFS_DEBUG
343                     printf("msdosfs_setattr(): returning EINVAL\n");
344                     printf("    va_type %d, va_nlink %x, va_fsid %"PRIx64", va_fileid %llx\n",
345                         vap->va_type, vap->va_nlink, vap->va_fsid,
346                         (unsigned long long)vap->va_fileid);
347                     printf("    va_blocksize %lx, va_rdev %"PRIx64", va_bytes %"PRIx64", va_gen %lx\n",
348                         vap->va_blocksize, vap->va_rdev, vap->va_bytes, vap->va_gen);
349 #endif
350                     return (EINVAL);
351           }
352           /*
353            * Silently ignore attributes modifications on directories.
354            */
355           if (ap->a_vp->v_type == VDIR)
356                     return 0;
357 
358           if (vap->va_size != VNOVAL) {
359                     if (vp->v_mount->mnt_flag & MNT_RDONLY) {
360                               error = EROFS;
361                               goto bad;
362                     }
363                     error = msdosfs_detrunc(dep, (u_long)vap->va_size, 0, cred);
364                     if (error)
365                               goto bad;
366                     de_changed = 1;
367           }
368           if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
369                     if (vp->v_mount->mnt_flag & MNT_RDONLY) {
370                               error = EROFS;
371                               goto bad;
372                     }
373                     error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_TIMES,
374                         ap->a_vp, NULL, genfs_can_chtimes(ap->a_vp, cred,
375                               pmp->pm_uid, vap->va_vaflags));
376                     if (error)
377                               goto bad;
378                     if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95) == 0 &&
379                         vap->va_atime.tv_sec != VNOVAL)
380                               msdosfs_unix2dostime(&vap->va_atime, pmp->pm_gmtoff,
381                                   &dep->de_ADate, NULL, NULL);
382                     if (vap->va_mtime.tv_sec != VNOVAL)
383                               msdosfs_unix2dostime(&vap->va_mtime, pmp->pm_gmtoff,
384                                   &dep->de_MDate, &dep->de_MTime, NULL);
385                     dep->de_Attributes |= ATTR_ARCHIVE;
386                     dep->de_flag |= DE_MODIFIED;
387                     de_changed = 1;
388           }
389           /*
390            * DOS files only have the ability to have their writability
391            * attribute set, so we use the owner write bit to set the readonly
392            * attribute.
393            */
394           if (vap->va_mode != (mode_t)VNOVAL) {
395                     if (vp->v_mount->mnt_flag & MNT_RDONLY) {
396                               error = EROFS;
397                               goto bad;
398                     }
399                     error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_FLAGS, vp,
400                         NULL, genfs_can_chflags(vp, cred, pmp->pm_uid, false));
401                     if (error)
402                               goto bad;
403                     /* We ignore the read and execute bits. */
404                     if (vap->va_mode & S_IWUSR)
405                               dep->de_Attributes &= ~ATTR_READONLY;
406                     else
407                               dep->de_Attributes |= ATTR_READONLY;
408                     dep->de_flag |= DE_MODIFIED;
409                     de_changed = 1;
410           }
411           /*
412            * Allow the `archived' bit to be toggled.
413            */
414           if (vap->va_flags != VNOVAL) {
415                     if (vp->v_mount->mnt_flag & MNT_RDONLY) {
416                               error = EROFS;
417                               goto bad;
418                     }
419                     error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_FLAGS, vp,
420                         NULL, genfs_can_chflags(vp, cred, pmp->pm_uid, false));
421                     if (error)
422                               goto bad;
423                     if (vap->va_flags & SF_ARCHIVED)
424                               dep->de_Attributes &= ~ATTR_ARCHIVE;
425                     else
426                               dep->de_Attributes |= ATTR_ARCHIVE;
427                     dep->de_flag |= DE_MODIFIED;
428                     de_changed = 1;
429           }
430 
431           if (de_changed) {
432                     error = msdosfs_deupdat(dep, 1);
433                     if (error)
434                               goto bad;
435           }
436 
437 bad:
438           return error;
439 }
440 
441 int
msdosfs_read(void * v)442 msdosfs_read(void *v)
443 {
444           struct vop_read_args /* {
445                     struct vnode *a_vp;
446                     struct uio *a_uio;
447                     int a_ioflag;
448                     kauth_cred_t a_cred;
449           } */ *ap = v;
450           int error = 0;
451           int64_t diff;
452           int blsize;
453           long n;
454           long on;
455           daddr_t lbn;
456           vsize_t bytelen;
457           struct buf *bp;
458           struct vnode *vp = ap->a_vp;
459           struct denode *dep = VTODE(vp);
460           struct msdosfsmount *pmp = dep->de_pmp;
461           struct uio *uio = ap->a_uio;
462 
463           /*
464            * If they didn't ask for any data, then we are done.
465            */
466 
467           if (uio->uio_resid == 0)
468                     return (0);
469           if (uio->uio_offset < 0)
470                     return (EINVAL);
471           if (uio->uio_offset >= dep->de_FileSize)
472                     return (0);
473 
474           if (vp->v_type == VREG) {
475                     const int advice = IO_ADV_DECODE(ap->a_ioflag);
476 
477                     while (uio->uio_resid > 0) {
478                               bytelen = MIN(dep->de_FileSize - uio->uio_offset,
479                                               uio->uio_resid);
480 
481                               if (bytelen == 0)
482                                         break;
483                               error = ubc_uiomove(&vp->v_uobj, uio, bytelen, advice,
484                                   UBC_READ | UBC_PARTIALOK | UBC_VNODE_FLAGS(vp));
485                               if (error)
486                                         break;
487                     }
488                     dep->de_flag |= DE_ACCESS;
489                     goto out;
490           }
491 
492           /* this loop is only for directories now */
493           do {
494                     lbn = de_cluster(pmp, uio->uio_offset);
495                     on = uio->uio_offset & pmp->pm_crbomask;
496                     n = MIN(pmp->pm_bpcluster - on, uio->uio_resid);
497                     if (uio->uio_offset >= dep->de_FileSize) {
498                               return (0);
499                     }
500                     /* file size (and hence diff) may be up to 4GB */
501                     diff = dep->de_FileSize - uio->uio_offset;
502                     if (diff < n)
503                               n = (long) diff;
504 
505                     /* convert cluster # to sector # */
506                     error = msdosfs_pcbmap(dep, lbn, &lbn, 0, &blsize);
507                     if (error)
508                               goto bad;
509 
510                     /*
511                      * If we are operating on a directory file then be sure to
512                      * do i/o with the vnode for the filesystem instead of the
513                      * vnode for the directory.
514                      */
515                     error = bread(pmp->pm_devvp, de_bn2kb(pmp, lbn), blsize,
516                         0, &bp);
517                     if (error) {
518                               goto bad;
519                     }
520                     n = MIN(n, pmp->pm_bpcluster - bp->b_resid);
521                     error = uiomove((char *)bp->b_data + on, (int) n, uio);
522                     brelse(bp, 0);
523           } while (error == 0 && uio->uio_resid > 0 && n != 0);
524 
525 out:
526           if ((ap->a_ioflag & IO_SYNC) == IO_SYNC) {
527                     int uerror;
528 
529                     uerror = msdosfs_deupdat(dep, 1);
530                     if (error == 0)
531                               error = uerror;
532           }
533 bad:
534           return (error);
535 }
536 
537 /*
538  * Write data to a file or directory.
539  */
540 int
msdosfs_write(void * v)541 msdosfs_write(void *v)
542 {
543           struct vop_write_args /* {
544                     struct vnode *a_vp;
545                     struct uio *a_uio;
546                     int a_ioflag;
547                     kauth_cred_t a_cred;
548           } */ *ap = v;
549           int resid;
550           int error = 0;
551           int ioflag = ap->a_ioflag;
552           u_long osize;
553           u_long count;
554           vsize_t bytelen;
555           off_t oldoff;
556           size_t rem;
557           struct uio *uio = ap->a_uio;
558           struct vnode *vp = ap->a_vp;
559           struct denode *dep = VTODE(vp);
560           struct msdosfsmount *pmp = dep->de_pmp;
561           kauth_cred_t cred = ap->a_cred;
562           bool async;
563 
564 #ifdef MSDOSFS_DEBUG
565           printf("msdosfs_write(vp %p, uio %p, ioflag %x, cred %p\n",
566               vp, uio, ioflag, cred);
567           printf("msdosfs_write(): diroff %lu, dirclust %lu, startcluster %lu\n",
568               dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster);
569 #endif
570 
571           switch (vp->v_type) {
572           case VREG:
573                     if (ioflag & IO_APPEND)
574                               uio->uio_offset = dep->de_FileSize;
575                     break;
576           case VDIR:
577                     return EISDIR;
578           default:
579                     panic("msdosfs_write(): bad file type");
580           }
581 
582           if (uio->uio_offset < 0)
583                     return (EINVAL);
584 
585           if (uio->uio_resid == 0)
586                     return (0);
587 
588           /* Don't bother to try to write files larger than the fs limit */
589           if (uio->uio_offset + uio->uio_resid > MSDOSFS_FILESIZE_MAX)
590                     return (EFBIG);
591 
592           /*
593            * If the offset we are starting the write at is beyond the end of
594            * the file, then they've done a seek.  Unix filesystems allow
595            * files with holes in them, DOS doesn't so we must fill the hole
596            * with zeroed blocks.
597            */
598           if (uio->uio_offset > dep->de_FileSize) {
599                     if ((error = msdosfs_deextend(dep, uio->uio_offset,
600                         cred)) != 0) {
601                               return (error);
602                     }
603           }
604 
605           /*
606            * Remember some values in case the write fails.
607            */
608           async = vp->v_mount->mnt_flag & MNT_ASYNC;
609           resid = uio->uio_resid;
610           osize = dep->de_FileSize;
611 
612           /*
613            * If we write beyond the end of the file, extend it to its ultimate
614            * size ahead of the time to hopefully get a contiguous area.
615            */
616           if (uio->uio_offset + resid > osize) {
617                     count = de_clcount(pmp, uio->uio_offset + resid) -
618                               de_clcount(pmp, osize);
619                     if ((error = msdosfs_extendfile(dep, count, NULL, NULL, 0)))
620                               goto errexit;
621 
622                     dep->de_FileSize = uio->uio_offset + resid;
623                     /* hint uvm to not read in extended part */
624                     uvm_vnp_setwritesize(vp, dep->de_FileSize);
625                     /* zero out the remainder of the last page */
626                     rem = round_page(dep->de_FileSize) - dep->de_FileSize;
627                     if (rem > 0)
628                               ubc_zerorange(&vp->v_uobj, (off_t)dep->de_FileSize,
629                                   rem, UBC_VNODE_FLAGS(vp));
630           }
631 
632           do {
633                     oldoff = uio->uio_offset;
634                     bytelen = uio->uio_resid;
635 
636                     error = ubc_uiomove(&vp->v_uobj, uio, bytelen,
637                         IO_ADV_DECODE(ioflag), UBC_WRITE | UBC_VNODE_FLAGS(vp));
638                     if (error)
639                               break;
640 
641                     /*
642                      * flush what we just wrote if necessary.
643                      * XXXUBC simplistic async flushing.
644                      */
645 
646                     if (!async && oldoff >> 16 != uio->uio_offset >> 16) {
647                               rw_enter(vp->v_uobj.vmobjlock, RW_WRITER);
648                               error = VOP_PUTPAGES(vp, (oldoff >> 16) << 16,
649                                   (uio->uio_offset >> 16) << 16,
650                                   PGO_CLEANIT | PGO_LAZY);
651                     }
652           } while (error == 0 && uio->uio_resid > 0);
653 
654           /* set final size */
655           uvm_vnp_setsize(vp, dep->de_FileSize);
656           if (error == 0 && ioflag & IO_SYNC) {
657                     rw_enter(vp->v_uobj.vmobjlock, RW_WRITER);
658                     error = VOP_PUTPAGES(vp, trunc_page(oldoff),
659                         round_page(oldoff + bytelen), PGO_CLEANIT | PGO_SYNCIO);
660           }
661           dep->de_flag |= DE_UPDATE;
662 
663           /*
664            * If the write failed and they want us to, truncate the file back
665            * to the size it was before the write was attempted.
666            */
667 errexit:
668           if (error) {
669                     msdosfs_detrunc(dep, osize, ioflag & IO_SYNC, NOCRED);
670                     uio->uio_offset -= resid - uio->uio_resid;
671                     uio->uio_resid = resid;
672           } else if ((ioflag & IO_SYNC) == IO_SYNC)
673                     error = msdosfs_deupdat(dep, 1);
674           KASSERT(vp->v_size == dep->de_FileSize);
675           return (error);
676 }
677 
678 int
msdosfs_update(struct vnode * vp,const struct timespec * acc,const struct timespec * mod,int flags)679 msdosfs_update(struct vnode *vp, const struct timespec *acc,
680     const struct timespec *mod, int flags)
681 {
682           struct buf *bp;
683           struct direntry *dirp;
684           struct denode *dep;
685           int error;
686 
687           if (vp->v_mount->mnt_flag & MNT_RDONLY)
688                     return (0);
689           dep = VTODE(vp);
690           DETIMES(dep, acc, mod, NULL, dep->de_pmp->pm_gmtoff);
691           if ((dep->de_flag & DE_MODIFIED) == 0)
692                     return (0);
693           dep->de_flag &= ~DE_MODIFIED;
694           if (dep->de_Attributes & ATTR_DIRECTORY)
695                     return (0);
696           if (dep->de_refcnt <= 0)
697                     return (0);
698           error = msdosfs_readde(dep, &bp, &dirp);
699           if (error)
700                     return (error);
701           DE_EXTERNALIZE(dirp, dep);
702           if (flags & (UPDATE_WAIT|UPDATE_DIROP))
703                     return (bwrite(bp));
704           else {
705                     bdwrite(bp);
706                     return (0);
707           }
708 }
709 
710 int
msdosfs_remove(void * v)711 msdosfs_remove(void *v)
712 {
713           struct vop_remove_v3_args /* {
714                     struct vnode *a_dvp;
715                     struct vnode *a_vp;
716                     struct componentname *a_cnp;
717                     nlink_t ctx_vp_new_nlink;
718           } */ *ap = v;
719           struct denode *dep = VTODE(ap->a_vp);
720           struct denode *ddep = VTODE(ap->a_dvp);
721           int error;
722 
723           if (ap->a_vp->v_type == VDIR)
724                     error = EPERM;
725           else
726                     error = msdosfs_removede(ddep, dep, &ddep->de_crap);
727 #ifdef MSDOSFS_DEBUG
728           printf("msdosfs_remove(), dep %p, usecount %d\n",
729                     dep, vrefcnt(ap->a_vp));
730 #endif
731           if (ddep == dep)
732                     vrele(ap->a_vp);
733           else
734                     vput(ap->a_vp);     /* causes msdosfs_inactive() to be called
735                                          * via vrele() */
736 
737           return (error);
738 }
739 
740 static const struct {
741           struct direntry dot;
742           struct direntry dotdot;
743 } dosdirtemplate = {
744           {         ".       ", "   ",                      /* the . entry */
745                     ATTR_DIRECTORY,                                   /* file attribute */
746                     0,                                                /* reserved */
747                     0, { 0, 0 }, { 0, 0 },                            /* create time & date */
748                     { 0, 0 },                               /* access date */
749                     { 0, 0 },                               /* high bits of start cluster */
750                     { 210, 4 }, { 210, 4 },                           /* modify time & date */
751                     { 0, 0 },                               /* startcluster */
752                     { 0, 0, 0, 0 }                                    /* filesize */
753           },
754           {         "..      ", "   ",                      /* the .. entry */
755                     ATTR_DIRECTORY,                                   /* file attribute */
756                     0,                                                /* reserved */
757                     0, { 0, 0 }, { 0, 0 },                            /* create time & date */
758                     { 0, 0 },                               /* access date */
759                     { 0, 0 },                               /* high bits of start cluster */
760                     { 210, 4 }, { 210, 4 },                           /* modify time & date */
761                     { 0, 0 },                               /* startcluster */
762                     { 0, 0, 0, 0 }                                    /* filesize */
763           }
764 };
765 
766 int
msdosfs_mkdir(void * v)767 msdosfs_mkdir(void *v)
768 {
769           struct vop_mkdir_v3_args /* {
770                     struct vnode *a_dvp;
771                     struvt vnode **a_vpp;
772                     struvt componentname *a_cnp;
773                     struct vattr *a_vap;
774           } */ *ap = v;
775           struct componentname *cnp = ap->a_cnp;
776           struct denode ndirent;
777           struct denode *dep;
778           struct denode *pdep = VTODE(ap->a_dvp);
779           int error;
780           int bn;
781           u_long newcluster, pcl;
782           daddr_t lbn;
783           struct direntry *denp;
784           struct msdosfsmount *pmp = pdep->de_pmp;
785           struct buf *bp;
786           int async = pdep->de_pmp->pm_mountp->mnt_flag & MNT_ASYNC;
787 
788           /*
789            * If this is the root directory and there is no space left we
790            * can't do anything.  This is because the root directory can not
791            * change size.
792            */
793           if (pdep->de_StartCluster == MSDOSFSROOT
794               && pdep->de_crap.mlr_fndoffset >= pdep->de_FileSize) {
795                     error = ENOSPC;
796                     goto bad2;
797           }
798 
799           /*
800            * Allocate a cluster to hold the about to be created directory.
801            */
802           error = msdosfs_clusteralloc(pmp, 0, 1, &newcluster, NULL);
803           if (error)
804                     goto bad2;
805 
806           memset(&ndirent, 0, sizeof(ndirent));
807           ndirent.de_pmp = pmp;
808           ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
809           DETIMES(&ndirent, NULL, NULL, NULL, pmp->pm_gmtoff);
810 
811           /*
812            * Now fill the cluster with the "." and ".." entries. And write
813            * the cluster to disk.  This way it is there for the parent
814            * directory to be pointing at if there were a crash.
815            */
816           bn = cntobn(pmp, newcluster);
817           lbn = de_bn2kb(pmp, bn);
818           /* always succeeds */
819           bp = getblk(pmp->pm_devvp, lbn, pmp->pm_bpcluster, 0, 0);
820           memset(bp->b_data, 0, pmp->pm_bpcluster);
821           memcpy(bp->b_data, &dosdirtemplate, sizeof dosdirtemplate);
822           denp = (struct direntry *)bp->b_data;
823           putushort(denp[0].deStartCluster, newcluster);
824           putushort(denp[0].deCDate, ndirent.de_CDate);
825           putushort(denp[0].deCTime, ndirent.de_CTime);
826           denp[0].deCHundredth = ndirent.de_CHun;
827           putushort(denp[0].deADate, ndirent.de_ADate);
828           putushort(denp[0].deMDate, ndirent.de_MDate);
829           putushort(denp[0].deMTime, ndirent.de_MTime);
830           pcl = pdep->de_StartCluster;
831           if (FAT32(pmp) && pcl == pmp->pm_rootdirblk)
832                     pcl = 0;
833           putushort(denp[1].deStartCluster, pcl);
834           putushort(denp[1].deCDate, ndirent.de_CDate);
835           putushort(denp[1].deCTime, ndirent.de_CTime);
836           denp[1].deCHundredth = ndirent.de_CHun;
837           putushort(denp[1].deADate, ndirent.de_ADate);
838           putushort(denp[1].deMDate, ndirent.de_MDate);
839           putushort(denp[1].deMTime, ndirent.de_MTime);
840           if (FAT32(pmp)) {
841                     putushort(denp[0].deHighClust, newcluster >> 16);
842                     putushort(denp[1].deHighClust, pdep->de_StartCluster >> 16);
843           } else {
844                     putushort(denp[0].deHighClust, 0);
845                     putushort(denp[1].deHighClust, 0);
846           }
847 
848           if (async)
849                     bdwrite(bp);
850           else if ((error = bwrite(bp)) != 0)
851                     goto bad;
852 
853           /*
854            * Now build up a directory entry pointing to the newly allocated
855            * cluster.  This will be written to an empty slot in the parent
856            * directory.
857            */
858           if ((error = msdosfs_uniqdosname(pdep, cnp, ndirent.de_Name)) != 0)
859                     goto bad;
860 
861           ndirent.de_Attributes = ATTR_DIRECTORY;
862           ndirent.de_StartCluster = newcluster;
863           ndirent.de_FileSize = 0;
864           ndirent.de_dev = pdep->de_dev;
865           ndirent.de_devvp = pdep->de_devvp;
866           if ((error = msdosfs_createde(&ndirent, pdep, &pdep->de_crap, &dep,
867               cnp)) != 0)
868                     goto bad;
869           *ap->a_vpp = DETOV(dep);
870           return (0);
871 
872 bad:
873           msdosfs_clusterfree(pmp, newcluster, NULL);
874 bad2:
875           return (error);
876 }
877 
878 int
msdosfs_rmdir(void * v)879 msdosfs_rmdir(void *v)
880 {
881           struct vop_rmdir_v2_args /* {
882                     struct vnode *a_dvp;
883                     struct vnode *a_vp;
884                     struct componentname *a_cnp;
885           } */ *ap = v;
886           struct vnode *vp = ap->a_vp;
887           struct vnode *dvp = ap->a_dvp;
888           struct componentname *cnp = ap->a_cnp;
889           struct denode *ip, *dp;
890           int error;
891 
892           ip = VTODE(vp);
893           dp = VTODE(dvp);
894           /*
895            * No rmdir "." please.
896            */
897           if (dp == ip) {
898                     vrele(vp);
899                     return (EINVAL);
900           }
901           /*
902            * Verify the directory is empty (and valid).
903            * (Rmdir ".." won't be valid since
904            *  ".." will contain a reference to
905            *  the current directory and thus be
906            *  non-empty.)
907            */
908           error = 0;
909           if (!msdosfs_dosdirempty(ip) || ip->de_flag & DE_RENAME) {
910                     error = ENOTEMPTY;
911                     goto out;
912           }
913           /*
914            * Delete the entry from the directory.  For dos filesystems this
915            * gets rid of the directory entry on disk, the in memory copy
916            * still exists but the de_refcnt is <= 0.  This prevents it from
917            * being found by deget().  When the vput() on dep is done we give
918            * up access and eventually msdosfs_reclaim() will be called which
919            * will remove it from the denode cache.
920            */
921           if ((error = msdosfs_removede(dp, ip, &dp->de_crap)) != 0)
922                     goto out;
923           /*
924            * This is where we decrement the link count in the parent
925            * directory.  Since dos filesystems don't do this we just purge
926            * the name cache and let go of the parent directory denode.
927            */
928           cache_purge(dvp);
929           /*
930            * Truncate the directory that is being deleted.
931            */
932           error = msdosfs_detrunc(ip, (u_long)0, IO_SYNC, cnp->cn_cred);
933           cache_purge(vp);
934 out:
935           vput(vp);
936           return (error);
937 }
938 
939 int
msdosfs_readdir(void * v)940 msdosfs_readdir(void *v)
941 {
942           struct vop_readdir_args /* {
943                     struct vnode *a_vp;
944                     struct uio *a_uio;
945                     kauth_cred_t a_cred;
946                     int *a_eofflag;
947                     off_t **a_cookies;
948                     int *a_ncookies;
949           } */ *ap = v;
950           int error = 0;
951           int diff;
952           long n;
953           int blsize;
954           long on;
955           long lost;
956           long count;
957           u_long cn;
958           ino_t fileno;
959           u_long dirsperblk;
960           long bias = 0;
961           daddr_t bn, lbn;
962           struct buf *bp;
963           struct denode *dep = VTODE(ap->a_vp);
964           struct msdosfsmount *pmp = dep->de_pmp;
965           struct direntry *dentp;
966           struct dirent *dirbuf;
967           struct uio *uio = ap->a_uio;
968           off_t *cookies = NULL;
969           int ncookies = 0, nc = 0;
970           off_t offset, uio_off;
971           int chksum = -1;
972           uint16_t namlen;
973 
974 #ifdef MSDOSFS_DEBUG
975           printf("msdosfs_readdir(): vp %p, uio %p, cred %p, eofflagp %p\n",
976               ap->a_vp, uio, ap->a_cred, ap->a_eofflag);
977 #endif
978 
979           /*
980            * msdosfs_readdir() won't operate properly on regular files since
981            * it does i/o only with the filesystem vnode, and hence can
982            * retrieve the wrong block from the buffer cache for a plain file.
983            * So, fail attempts to readdir() on a plain file.
984            */
985           if ((dep->de_Attributes & ATTR_DIRECTORY) == 0)
986                     return (ENOTDIR);
987 
988           /*
989            * If the user buffer is smaller than the size of one dos directory
990            * entry or the file offset is not a multiple of the size of a
991            * directory entry, then we fail the read.
992            */
993           count = uio->uio_resid & ~(sizeof(struct direntry) - 1);
994           offset = uio->uio_offset;
995           if (count < sizeof(struct direntry) ||
996               (offset & (sizeof(struct direntry) - 1)))
997                     return (EINVAL);
998           lost = uio->uio_resid - count;
999           uio->uio_resid = count;
1000           uio_off = uio->uio_offset;
1001 
1002 
1003           /* Allocate a temporary dirent buffer. */
1004           dirbuf = malloc(sizeof(struct dirent), M_MSDOSFSTMP, M_WAITOK | M_ZERO);
1005 
1006           if (ap->a_ncookies) {
1007                     nc = uio->uio_resid / _DIRENT_MINSIZE((struct dirent *)0);
1008                     cookies = malloc(nc * sizeof (off_t), M_TEMP, M_WAITOK);
1009                     *ap->a_cookies = cookies;
1010           }
1011 
1012           dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry);
1013 
1014           /*
1015            * If they are reading from the root directory then, we simulate
1016            * the . and .. entries since these don't exist in the root
1017            * directory.  We also set the offset bias to make up for having to
1018            * simulate these entries. By this I mean that at file offset 64 we
1019            * read the first entry in the root directory that lives on disk.
1020            */
1021           if (dep->de_StartCluster == MSDOSFSROOT
1022               || (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk)) {
1023 #if 0
1024                     printf("msdosfs_readdir(): going after . or .. in root dir, "
1025                         "offset %" PRIu64 "\n", offset);
1026 #endif
1027                     bias = 2 * sizeof(struct direntry);
1028                     if (offset < bias) {
1029                               for (n = (int)offset / sizeof(struct direntry);
1030                                    n < 2; n++) {
1031                                         if (FAT32(pmp))
1032                                                   dirbuf->d_fileno = cntobn(pmp,
1033                                                        (ino_t)pmp->pm_rootdirblk)
1034                                                        * dirsperblk;
1035                                         else
1036                                                   dirbuf->d_fileno = 1;
1037                                         dirbuf->d_type = DT_DIR;
1038                                         switch (n) {
1039                                         case 0:
1040                                                   dirbuf->d_namlen = 1;
1041                                                   strlcpy(dirbuf->d_name, ".",
1042                                                       sizeof(dirbuf->d_name));
1043                                                   break;
1044                                         case 1:
1045                                                   dirbuf->d_namlen = 2;
1046                                                   strlcpy(dirbuf->d_name, "..",
1047                                                       sizeof(dirbuf->d_name));
1048                                                   break;
1049                                         }
1050                                         dirbuf->d_reclen = _DIRENT_SIZE(dirbuf);
1051                                         if (uio->uio_resid < dirbuf->d_reclen)
1052                                                   goto out;
1053                                         error = uiomove(dirbuf, dirbuf->d_reclen, uio);
1054                                         if (error)
1055                                                   goto out;
1056                                         offset += sizeof(struct direntry);
1057                                         uio_off = offset;
1058                                         if (cookies) {
1059                                                   *cookies++ = offset;
1060                                                   ncookies++;
1061                                                   if (ncookies >= nc)
1062                                                             goto out;
1063                                         }
1064                               }
1065                     }
1066           }
1067 
1068           while (uio->uio_resid > 0) {
1069                     lbn = de_cluster(pmp, offset - bias);
1070                     on = (offset - bias) & pmp->pm_crbomask;
1071                     n = MIN(pmp->pm_bpcluster - on, uio->uio_resid);
1072                     diff = dep->de_FileSize - (offset - bias);
1073                     if (diff <= 0)
1074                               break;
1075                     n = MIN(n, diff);
1076                     if ((error = msdosfs_pcbmap(dep, lbn, &bn, &cn, &blsize)) != 0)
1077                               break;
1078                     error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
1079                         0, &bp);
1080                     if (error) {
1081                               goto bad;
1082                     }
1083                     n = MIN(n, blsize - bp->b_resid);
1084 
1085                     /*
1086                      * Convert from dos directory entries to fs-independent
1087                      * directory entries.
1088                      */
1089                     for (dentp = (struct direntry *)((char *)bp->b_data + on);
1090                          (char *)dentp < (char *)bp->b_data + on + n;
1091                          dentp++, offset += sizeof(struct direntry)) {
1092 #if 0
1093 
1094                               printf("rd: dentp %08x prev %08x crnt %08x deName %02x attr %02x\n",
1095                                   dentp, prev, crnt, dentp->deName[0], dentp->deAttributes);
1096 #endif
1097                               /*
1098                                * If this is an unused entry, we can stop.
1099                                */
1100                               if (dentp->deName[0] == SLOT_EMPTY) {
1101                                         brelse(bp, 0);
1102                                         goto out;
1103                               }
1104                               /*
1105                                * Skip deleted entries.
1106                                */
1107                               if (dentp->deName[0] == SLOT_DELETED) {
1108                                         chksum = -1;
1109                                         continue;
1110                               }
1111 
1112                               /*
1113                                * Handle Win95 long directory entries
1114                                */
1115                               if (dentp->deAttributes == ATTR_WIN95) {
1116                                         if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
1117                                                   continue;
1118                                         chksum =
1119                                             msdosfs_win2unixfn((struct winentry *)dentp,
1120                                             dirbuf, chksum, &namlen,
1121                                             pmp->pm_flags & MSDOSFSMNT_UTF8);
1122                                         if (chksum != -1)
1123                                                   dirbuf->d_namlen = namlen;
1124                                         continue;
1125                               }
1126 
1127                               /*
1128                                * Skip volume labels
1129                                */
1130                               if (dentp->deAttributes & ATTR_VOLUME) {
1131                                         chksum = -1;
1132                                         continue;
1133                               }
1134                               /*
1135                                * This computation of d_fileno must match
1136                                * the computation of va_fileid in
1137                                * msdosfs_getattr.
1138                                */
1139                               if (dentp->deAttributes & ATTR_DIRECTORY) {
1140                                         fileno = getushort(dentp->deStartCluster);
1141                                         if (FAT32(pmp))
1142                                                   fileno |= ((ino_t)getushort(dentp->deHighClust)) << 16;
1143                                         /* if this is the root directory */
1144                                         if (fileno == MSDOSFSROOT)
1145                                                   if (FAT32(pmp))
1146                                                             fileno = cntobn(pmp,
1147                                                                 (ino_t)pmp->pm_rootdirblk)
1148                                                                 * dirsperblk;
1149                                                   else
1150                                                             fileno = 1;
1151                                         else
1152                                                   fileno = cntobn(pmp, fileno) * dirsperblk;
1153                                         dirbuf->d_fileno = fileno;
1154                                         dirbuf->d_type = DT_DIR;
1155                               } else {
1156                                         dirbuf->d_fileno =
1157                                             offset / sizeof(struct direntry);
1158                                         dirbuf->d_type = DT_REG;
1159                               }
1160                               if (chksum != msdosfs_winChksum(dentp->deName)) {
1161                                         char deName[11];
1162 
1163                                         memcpy(deName, dentp->deName,
1164                                                sizeof dentp->deName);
1165                                         memcpy(deName + 8, dentp->deExtension,
1166                                                sizeof dentp->deExtension);
1167                                         assert(sizeof(deName) == sizeof(dentp->deName) +
1168                                                   sizeof(dentp->deExtension));
1169                                         dirbuf->d_namlen =
1170                                             msdosfs_dos2unixfn(deName,
1171                                                 (u_char *)dirbuf->d_name,
1172                                                 pmp->pm_flags & MSDOSFSMNT_SHORTNAME);
1173                               } else
1174                                         dirbuf->d_name[dirbuf->d_namlen] = 0;
1175                               namlen = dirbuf->d_namlen;
1176                               chksum = -1;
1177                               dirbuf->d_reclen = _DIRENT_SIZE(dirbuf);
1178                               if (uio->uio_resid < dirbuf->d_reclen) {
1179                                         brelse(bp, 0);
1180                                         goto out;
1181                               }
1182                               error = uiomove(dirbuf, dirbuf->d_reclen, uio);
1183                               if (error) {
1184                                         brelse(bp, 0);
1185                                         goto out;
1186                               }
1187                               uio_off = offset + sizeof(struct direntry);
1188                               if (cookies) {
1189                                         *cookies++ = offset + sizeof(struct direntry);
1190                                         ncookies++;
1191                                         if (ncookies >= nc) {
1192                                                   brelse(bp, 0);
1193                                                   goto out;
1194                                         }
1195                               }
1196                     }
1197                     brelse(bp, 0);
1198           }
1199 
1200 out:
1201           uio->uio_offset = uio_off;
1202           uio->uio_resid += lost;
1203           if (dep->de_FileSize - (offset - bias) <= 0)
1204                     *ap->a_eofflag = 1;
1205           else
1206                     *ap->a_eofflag = 0;
1207 
1208           if (ap->a_ncookies) {
1209                     if (error) {
1210                               free(*ap->a_cookies, M_TEMP);
1211                               *ap->a_ncookies = 0;
1212                               *ap->a_cookies = NULL;
1213                     } else
1214                               *ap->a_ncookies = ncookies;
1215           }
1216 
1217 bad:
1218           free(dirbuf, M_MSDOSFSTMP);
1219           return (error);
1220 }
1221 
1222 /*
1223  * vp  - address of vnode file the file
1224  * bn  - which cluster we are interested in mapping to a filesystem block number.
1225  * vpp - returns the vnode for the block special file holding the filesystem
1226  *         containing the file of interest
1227  * bnp - address of where to return the filesystem relative block number
1228  */
1229 int
msdosfs_bmap(void * v)1230 msdosfs_bmap(void *v)
1231 {
1232           struct vop_bmap_args /* {
1233                     struct vnode *a_vp;
1234                     daddr_t a_bn;
1235                     struct vnode **a_vpp;
1236                     daddr_t *a_bnp;
1237                     int *a_runp;
1238           } */ *ap = v;
1239           struct denode *dep = VTODE(ap->a_vp);
1240           int run, maxrun;
1241           daddr_t runbn;
1242           int status;
1243 
1244           if (ap->a_vpp != NULL)
1245                     *ap->a_vpp = dep->de_devvp;
1246           if (ap->a_bnp == NULL)
1247                     return (0);
1248           status = msdosfs_pcbmap(dep, ap->a_bn, ap->a_bnp, 0, 0);
1249 
1250           /*
1251            * From FreeBSD:
1252            * A little kludgy, but we loop calling pcbmap until we
1253            * reach the end of the contiguous piece, or reach MAXPHYS.
1254            * Since it reduces disk I/Os, the "wasted" CPU is put to
1255            * good use (4 to 5 fold sequential read I/O improvement on USB
1256            * drives).
1257            */
1258           if (ap->a_runp != NULL) {
1259                     /* taken from ufs_bmap */
1260                     maxrun = ulmin(MAXPHYS / dep->de_pmp->pm_bpcluster - 1,
1261                                      dep->de_pmp->pm_maxcluster - ap->a_bn);
1262                     for (run = 1; run <= maxrun; run++) {
1263                               if (msdosfs_pcbmap(dep, ap->a_bn + run, &runbn, NULL,
1264                                   NULL) != 0 || runbn !=
1265                                           *ap->a_bnp + de_cn2bn(dep->de_pmp, run))
1266                                         break;
1267                     }
1268                     *ap->a_runp = run - 1;
1269           }
1270 
1271           /*
1272            * We need to scale *ap->a_bnp by sector_size/DEV_BSIZE
1273            */
1274           *ap->a_bnp = de_bn2kb(dep->de_pmp, *ap->a_bnp);
1275           return status;
1276 }
1277 
1278 int
msdosfs_strategy(void * v)1279 msdosfs_strategy(void *v)
1280 {
1281           struct vop_strategy_args /* {
1282                     struct vnode *a_vp;
1283                     struct buf *a_bp;
1284           } */ *ap = v;
1285           struct vnode *vp = ap->a_vp;
1286           struct buf *bp = ap->a_bp;
1287           struct denode *dep = VTODE(bp->b_vp);
1288           int error = 0;
1289 
1290           if (vp->v_type == VBLK || vp->v_type == VCHR)
1291                     panic("msdosfs_strategy: spec");
1292           /*
1293            * If we don't already know the filesystem relative block number
1294            * then get it using pcbmap().  If pcbmap() returns the block
1295            * number as -1 then we've got a hole in the file.  DOS filesystems
1296            * don't allow files with holes, so we shouldn't ever see this.
1297            */
1298           if (bp->b_blkno == bp->b_lblkno) {
1299                     error = msdosfs_pcbmap(dep, de_bn2cn(dep->de_pmp, bp->b_lblkno),
1300                                      &bp->b_blkno, 0, 0);
1301                     if (error)
1302                               bp->b_blkno = -1;
1303                     if (bp->b_blkno == -1)
1304                               clrbuf(bp);
1305                     else
1306                               bp->b_blkno = de_bn2kb(dep->de_pmp, bp->b_blkno);
1307           }
1308           if (bp->b_blkno == -1) {
1309                     biodone(bp);
1310                     return (error);
1311           }
1312 
1313           /*
1314            * Read/write the block from/to the disk that contains the desired
1315            * file block.
1316            */
1317 
1318           vp = dep->de_devvp;
1319           return (VOP_STRATEGY(vp, bp));
1320 }
1321 
1322 int
msdosfs_print(void * v)1323 msdosfs_print(void *v)
1324 {
1325           struct vop_print_args /* {
1326                     struct vnode *vp;
1327           } */ *ap = v;
1328           struct denode *dep = VTODE(ap->a_vp);
1329 
1330           printf(
1331               "tag VT_MSDOSFS, startcluster %ld, dircluster %ld, diroffset %ld ",
1332               dep->de_StartCluster, dep->de_dirclust, dep->de_diroffset);
1333           printf(" dev %llu, %llu ", (unsigned long long)major(dep->de_dev),
1334               (unsigned long long)minor(dep->de_dev));
1335           printf("\n");
1336           return (0);
1337 }
1338 
1339 int
msdosfs_advlock(void * v)1340 msdosfs_advlock(void *v)
1341 {
1342           struct vop_advlock_args /* {
1343                     struct vnode *a_vp;
1344                     void *a_id;
1345                     int a_op;
1346                     struct flock *a_fl;
1347                     int a_flags;
1348           } */ *ap = v;
1349           struct denode *dep = VTODE(ap->a_vp);
1350 
1351           return lf_advlock(ap, &dep->de_lockf, dep->de_FileSize);
1352 }
1353 
1354 int
msdosfs_pathconf(void * v)1355 msdosfs_pathconf(void *v)
1356 {
1357           struct vop_pathconf_args /* {
1358                     struct vnode *a_vp;
1359                     int a_name;
1360                     register_t *a_retval;
1361           } */ *ap = v;
1362 
1363           switch (ap->a_name) {
1364           case _PC_LINK_MAX:
1365                     *ap->a_retval = 1;
1366                     return (0);
1367           case _PC_NAME_MAX:
1368                     *ap->a_retval = ap->a_vp->v_mount->mnt_stat.f_namemax;
1369                     return (0);
1370           case _PC_PATH_MAX:
1371                     *ap->a_retval = PATH_MAX;
1372                     return (0);
1373           case _PC_CHOWN_RESTRICTED:
1374                     *ap->a_retval = 1;
1375                     return (0);
1376           case _PC_NO_TRUNC:
1377                     *ap->a_retval = 1;
1378                     return (0);
1379           case _PC_SYNC_IO:
1380                     *ap->a_retval = 1;
1381                     return (0);
1382           case _PC_FILESIZEBITS:
1383                     *ap->a_retval = 32;
1384                     return (0);
1385           default:
1386                     return genfs_pathconf(ap);
1387           }
1388           /* NOTREACHED */
1389 }
1390 
1391 /*
1392  * Flush the blocks of a file to disk.
1393  *
1394  * This function is worthless for vnodes that represent directories. Maybe we
1395  * could just do a sync if they try an fsync on a directory file.
1396  */
1397 int
msdosfs_fsync(void * v)1398 msdosfs_fsync(void *v)
1399 {
1400           struct vop_fsync_args /* {
1401                     struct vnode *a_vp;
1402                     kauth_cred_t a_cred;
1403                     int a_flags;
1404                     off_t offlo;
1405                     off_t offhi;
1406           } */ *ap = v;
1407           struct vnode *vp = ap->a_vp;
1408           int wait;
1409           int error;
1410 
1411           wait = (ap->a_flags & FSYNC_WAIT) != 0;
1412           error = vflushbuf(vp, ap->a_flags);
1413           if (error == 0 && (ap->a_flags & FSYNC_DATAONLY) == 0)
1414                     error = msdosfs_update(vp, NULL, NULL, wait ? UPDATE_WAIT : 0);
1415 
1416           if (error == 0 && ap->a_flags & FSYNC_CACHE) {
1417                     struct denode *dep = VTODE(vp);
1418                     struct vnode *devvp = dep->de_devvp;
1419 
1420                     int l = 0;
1421                     error = VOP_IOCTL(devvp, DIOCCACHESYNC, &l, FWRITE,
1422                                                     curlwp->l_cred);
1423           }
1424 
1425           return (error);
1426 }
1427 
1428 void
msdosfs_detimes(struct denode * dep,const struct timespec * acc,const struct timespec * mod,const struct timespec * cre,int gmtoff)1429 msdosfs_detimes(struct denode *dep, const struct timespec *acc,
1430     const struct timespec *mod, const struct timespec *cre, int gmtoff)
1431 {
1432           struct timespec *ts = NULL, tsb;
1433 
1434           KASSERT(dep->de_flag & (DE_UPDATE | DE_CREATE | DE_ACCESS));
1435           /* XXX just call getnanotime early and use result if needed? */
1436           dep->de_flag |= DE_MODIFIED;
1437           if (dep->de_flag & DE_UPDATE) {
1438                     if (mod == NULL) {
1439                               getnanotime(&tsb);
1440                               mod = ts = &tsb;
1441                     }
1442                     msdosfs_unix2dostime(mod, gmtoff, &dep->de_MDate,
1443                         &dep->de_MTime, NULL);
1444                     dep->de_Attributes |= ATTR_ARCHIVE;
1445           }
1446           if ((dep->de_pmp->pm_flags & MSDOSFSMNT_NOWIN95) == 0) {
1447                     if (dep->de_flag & DE_ACCESS)  {
1448                               if (acc == NULL)
1449                                         acc = ts == NULL ?
1450                                             (getnanotime(&tsb), ts = &tsb) : ts;
1451                               msdosfs_unix2dostime(acc, gmtoff, &dep->de_ADate,
1452                                   NULL, NULL);
1453                     }
1454                     if (dep->de_flag & DE_CREATE) {
1455                               if (cre == NULL)
1456                                         cre = ts == NULL ?
1457                                             (getnanotime(&tsb), ts = &tsb) : ts;
1458                               msdosfs_unix2dostime(cre, gmtoff, &dep->de_CDate,
1459                                   &dep->de_CTime, &dep->de_CHun);
1460                     }
1461           }
1462 
1463           dep->de_flag &= ~(DE_UPDATE | DE_CREATE | DE_ACCESS);
1464 }
1465 
1466 /* Global vfs data structures for msdosfs */
1467 int (**msdosfs_vnodeop_p)(void *);
1468 const struct vnodeopv_entry_desc msdosfs_vnodeop_entries[] = {
1469           { &vop_default_desc, vn_default_error },
1470           { &vop_parsepath_desc, genfs_parsepath },         /* parsepath */
1471           { &vop_lookup_desc, msdosfs_lookup },             /* lookup */
1472           { &vop_create_desc, msdosfs_create },             /* create */
1473           { &vop_mknod_desc, genfs_eopnotsupp },            /* mknod */
1474           { &vop_open_desc, genfs_nullop },                 /* open */
1475           { &vop_close_desc, msdosfs_close },               /* close */
1476           { &vop_access_desc, msdosfs_access },             /* access */
1477           { &vop_accessx_desc, genfs_accessx },             /* accessx */
1478           { &vop_getattr_desc, msdosfs_getattr },           /* getattr */
1479           { &vop_setattr_desc, msdosfs_setattr },           /* setattr */
1480           { &vop_read_desc, msdosfs_read },                 /* read */
1481           { &vop_write_desc, msdosfs_write },               /* write */
1482           { &vop_fallocate_desc, genfs_eopnotsupp },        /* fallocate */
1483           { &vop_fdiscard_desc, genfs_eopnotsupp },         /* fdiscard */
1484           { &vop_fcntl_desc, genfs_fcntl },                 /* fcntl */
1485           { &vop_ioctl_desc, genfs_enoioctl },              /* ioctl */
1486           { &vop_poll_desc, genfs_poll },                             /* poll */
1487           { &vop_kqfilter_desc, genfs_kqfilter },           /* kqfilter */
1488           { &vop_revoke_desc, genfs_revoke },               /* revoke */
1489           { &vop_mmap_desc, genfs_mmap },                             /* mmap */
1490           { &vop_fsync_desc, msdosfs_fsync },               /* fsync */
1491           { &vop_seek_desc, genfs_seek },                             /* seek */
1492           { &vop_remove_desc, msdosfs_remove },             /* remove */
1493           { &vop_link_desc, genfs_eopnotsupp },             /* link */
1494           { &vop_rename_desc, msdosfs_rename },             /* rename */
1495           { &vop_mkdir_desc, msdosfs_mkdir },               /* mkdir */
1496           { &vop_rmdir_desc, msdosfs_rmdir },               /* rmdir */
1497           { &vop_symlink_desc, genfs_eopnotsupp },          /* symlink */
1498           { &vop_readdir_desc, msdosfs_readdir },           /* readdir */
1499           { &vop_readlink_desc, genfs_einval },             /* readlink */
1500           { &vop_abortop_desc, genfs_abortop },             /* abortop */
1501           { &vop_inactive_desc, msdosfs_inactive },         /* inactive */
1502           { &vop_reclaim_desc, msdosfs_reclaim },           /* reclaim */
1503           { &vop_lock_desc, genfs_lock },                             /* lock */
1504           { &vop_unlock_desc, genfs_unlock },               /* unlock */
1505           { &vop_bmap_desc, msdosfs_bmap },                 /* bmap */
1506           { &vop_strategy_desc, msdosfs_strategy },         /* strategy */
1507           { &vop_print_desc, msdosfs_print },               /* print */
1508           { &vop_islocked_desc, genfs_islocked },           /* islocked */
1509           { &vop_pathconf_desc, msdosfs_pathconf },         /* pathconf */
1510           { &vop_advlock_desc, msdosfs_advlock },           /* advlock */
1511           { &vop_bwrite_desc, vn_bwrite },                  /* bwrite */
1512           { &vop_getpages_desc, genfs_getpages },           /* getpages */
1513           { &vop_putpages_desc, genfs_putpages },           /* putpages */
1514           { NULL, NULL }
1515 };
1516 const struct vnodeopv_desc msdosfs_vnodeop_opv_desc =
1517           { &msdosfs_vnodeop_p, msdosfs_vnodeop_entries };
1518