msdosfs_vnops.c revision 1.69 1 /* $NetBSD: msdosfs_vnops.c,v 1.69 2010/11/30 10:29:59 dholland 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 (at) 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 (at) 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.69 2010/11/30 10:29:59 dholland 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/fstrans.h>
64 #include <sys/vnode.h>
65 #include <sys/signalvar.h>
66 #include <sys/malloc.h>
67 #include <sys/dirent.h>
68 #include <sys/lockf.h>
69 #include <sys/kauth.h>
70
71 #include <miscfs/genfs/genfs.h>
72 #include <miscfs/specfs/specdev.h> /* XXX */ /* defines v_rdev */
73
74 #include <uvm/uvm_extern.h>
75
76 #include <fs/msdosfs/bpb.h>
77 #include <fs/msdosfs/direntry.h>
78 #include <fs/msdosfs/denode.h>
79 #include <fs/msdosfs/msdosfsmount.h>
80 #include <fs/msdosfs/fat.h>
81
82 /*
83 * Some general notes:
84 *
85 * In the ufs filesystem the inodes, superblocks, and indirect blocks are
86 * read/written using the vnode for the filesystem. Blocks that represent
87 * the contents of a file are read/written using the vnode for the file
88 * (including directories when they are read/written as files). This
89 * presents problems for the dos filesystem because data that should be in
90 * an inode (if dos had them) resides in the directory itself. Since we
91 * must update directory entries without the benefit of having the vnode
92 * for the directory we must use the vnode for the filesystem. This means
93 * that when a directory is actually read/written (via read, write, or
94 * readdir, or seek) we must use the vnode for the filesystem instead of
95 * the vnode for the directory as would happen in ufs. This is to insure we
96 * retrieve the correct block from the buffer cache since the hash value is
97 * based upon the vnode address and the desired block number.
98 */
99
100 /*
101 * Create a regular file. On entry the directory to contain the file being
102 * created is locked. We must release before we return.
103 */
104 int
105 msdosfs_create(void *v)
106 {
107 struct vop_create_args /* {
108 struct vnode *a_dvp;
109 struct vnode **a_vpp;
110 struct componentname *a_cnp;
111 struct vattr *a_vap;
112 } */ *ap = v;
113 struct componentname *cnp = ap->a_cnp;
114 struct denode ndirent;
115 struct denode *dep;
116 struct denode *pdep = VTODE(ap->a_dvp);
117 int error;
118
119 #ifdef MSDOSFS_DEBUG
120 printf("msdosfs_create(cnp %p, vap %p\n", cnp, ap->a_vap);
121 #endif
122
123 fstrans_start(ap->a_dvp->v_mount, FSTRANS_SHARED);
124 /*
125 * If this is the root directory and there is no space left we
126 * can't do anything. This is because the root directory can not
127 * change size.
128 */
129 if (pdep->de_StartCluster == MSDOSFSROOT
130 && pdep->de_fndoffset >= pdep->de_FileSize) {
131 error = ENOSPC;
132 goto bad;
133 }
134
135 /*
136 * Create a directory entry for the file, then call createde() to
137 * have it installed. NOTE: DOS files are always executable. We
138 * use the absence of the owner write bit to make the file
139 * readonly.
140 */
141 #ifdef DIAGNOSTIC
142 if ((cnp->cn_flags & HASBUF) == 0)
143 panic("msdosfs_create: no name");
144 #endif
145 memset(&ndirent, 0, sizeof(ndirent));
146 if ((error = uniqdosname(pdep, cnp, ndirent.de_Name)) != 0)
147 goto bad;
148
149 ndirent.de_Attributes = (ap->a_vap->va_mode & S_IWUSR) ?
150 ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY;
151 ndirent.de_StartCluster = 0;
152 ndirent.de_FileSize = 0;
153 ndirent.de_dev = pdep->de_dev;
154 ndirent.de_devvp = pdep->de_devvp;
155 ndirent.de_pmp = pdep->de_pmp;
156 ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
157 DETIMES(&ndirent, NULL, NULL, NULL, pdep->de_pmp->pm_gmtoff);
158 if ((error = createde(&ndirent, pdep, &dep, cnp)) != 0)
159 goto bad;
160 fstrans_done(ap->a_dvp->v_mount);
161 VN_KNOTE(ap->a_dvp, NOTE_WRITE);
162 vput(ap->a_dvp);
163 *ap->a_vpp = DETOV(dep);
164 return (0);
165
166 bad:
167 fstrans_done(ap->a_dvp->v_mount);
168 vput(ap->a_dvp);
169 return (error);
170 }
171
172 int
173 msdosfs_close(void *v)
174 {
175 struct vop_close_args /* {
176 struct vnode *a_vp;
177 int a_fflag;
178 kauth_cred_t a_cred;
179 } */ *ap = v;
180 struct vnode *vp = ap->a_vp;
181 struct denode *dep = VTODE(vp);
182
183 fstrans_start(vp->v_mount, FSTRANS_SHARED);
184 mutex_enter(&vp->v_interlock);
185 if (vp->v_usecount > 1)
186 DETIMES(dep, NULL, NULL, NULL, dep->de_pmp->pm_gmtoff);
187 mutex_exit(&vp->v_interlock);
188 fstrans_done(vp->v_mount);
189 return (0);
190 }
191
192 static int
193 msdosfs_check_possible(struct vnode *vp, struct denode *dep, mode_t mode)
194 {
195
196 /*
197 * Disallow write attempts on read-only file systems;
198 * unless the file is a socket, fifo, or a block or
199 * character device resident on the file system.
200 */
201 if (mode & VWRITE) {
202 switch (vp->v_type) {
203 case VDIR:
204 case VLNK:
205 case VREG:
206 if (vp->v_mount->mnt_flag & MNT_RDONLY)
207 return (EROFS);
208 default:
209 break;
210 }
211 }
212
213 return 0;
214 }
215
216 static int
217 msdosfs_check_permitted(struct vnode *vp, struct denode *dep, mode_t mode,
218 kauth_cred_t cred)
219 {
220 struct msdosfsmount *pmp = dep->de_pmp;
221 mode_t file_mode;
222
223 if ((dep->de_Attributes & ATTR_READONLY) == 0)
224 file_mode = S_IRWXU|S_IRWXG|S_IRWXO;
225 else
226 file_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
227
228 return genfs_can_access(vp->v_type,
229 file_mode & (vp->v_type == VDIR ? pmp->pm_dirmask : pmp->pm_mask),
230 pmp->pm_uid, pmp->pm_gid, mode, cred);
231 }
232
233 int
234 msdosfs_access(void *v)
235 {
236 struct vop_access_args /* {
237 struct vnode *a_vp;
238 int a_mode;
239 kauth_cred_t a_cred;
240 } */ *ap = v;
241 struct vnode *vp = ap->a_vp;
242 struct denode *dep = VTODE(vp);
243 int error;
244
245 error = msdosfs_check_possible(vp, dep, ap->a_mode);
246 if (error)
247 return error;
248
249 error = msdosfs_check_permitted(vp, dep, ap->a_mode, ap->a_cred);
250
251 return error;
252 }
253
254 int
255 msdosfs_getattr(void *v)
256 {
257 struct vop_getattr_args /* {
258 struct vnode *a_vp;
259 struct vattr *a_vap;
260 kauth_cred_t a_cred;
261 } */ *ap = v;
262 struct denode *dep = VTODE(ap->a_vp);
263 struct msdosfsmount *pmp = dep->de_pmp;
264 struct vattr *vap = ap->a_vap;
265 mode_t mode;
266 u_long dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry);
267 ino_t fileid;
268
269 fstrans_start(ap->a_vp->v_mount, FSTRANS_SHARED);
270 DETIMES(dep, NULL, NULL, NULL, pmp->pm_gmtoff);
271 vap->va_fsid = dep->de_dev;
272 /*
273 * The following computation of the fileid must be the same as that
274 * used in msdosfs_readdir() to compute d_fileno. If not, pwd
275 * doesn't work.
276 */
277 if (dep->de_Attributes & ATTR_DIRECTORY) {
278 fileid = cntobn(pmp, (ino_t)dep->de_StartCluster) * dirsperblk;
279 if (dep->de_StartCluster == MSDOSFSROOT)
280 fileid = 1;
281 } else {
282 fileid = cntobn(pmp, (ino_t)dep->de_dirclust) * dirsperblk;
283 if (dep->de_dirclust == MSDOSFSROOT)
284 fileid = roottobn(pmp, 0) * dirsperblk;
285 fileid += dep->de_diroffset / sizeof(struct direntry);
286 }
287 vap->va_fileid = fileid;
288 if ((dep->de_Attributes & ATTR_READONLY) == 0)
289 mode = S_IRWXU|S_IRWXG|S_IRWXO;
290 else
291 mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
292 vap->va_mode =
293 mode & (ap->a_vp->v_type == VDIR ? pmp->pm_dirmask : pmp->pm_mask);
294 vap->va_uid = pmp->pm_uid;
295 vap->va_gid = pmp->pm_gid;
296 vap->va_nlink = 1;
297 vap->va_rdev = 0;
298 vap->va_size = ap->a_vp->v_size;
299 dos2unixtime(dep->de_MDate, dep->de_MTime, 0, pmp->pm_gmtoff,
300 &vap->va_mtime);
301 if (dep->de_pmp->pm_flags & MSDOSFSMNT_LONGNAME) {
302 dos2unixtime(dep->de_ADate, 0, 0, pmp->pm_gmtoff,
303 &vap->va_atime);
304 dos2unixtime(dep->de_CDate, dep->de_CTime, dep->de_CHun,
305 pmp->pm_gmtoff, &vap->va_ctime);
306 } else {
307 vap->va_atime = vap->va_mtime;
308 vap->va_ctime = vap->va_mtime;
309 }
310 vap->va_flags = 0;
311 if ((dep->de_Attributes & ATTR_ARCHIVE) == 0)
312 vap->va_mode |= S_ARCH1;
313 vap->va_gen = 0;
314 vap->va_blocksize = pmp->pm_bpcluster;
315 vap->va_bytes =
316 (dep->de_FileSize + pmp->pm_crbomask) & ~pmp->pm_crbomask;
317 vap->va_type = ap->a_vp->v_type;
318 fstrans_done(ap->a_vp->v_mount);
319 return (0);
320 }
321
322 int
323 msdosfs_setattr(void *v)
324 {
325 struct vop_setattr_args /* {
326 struct vnode *a_vp;
327 struct vattr *a_vap;
328 kauth_cred_t a_cred;
329 } */ *ap = v;
330 int error = 0, de_changed = 0;
331 struct denode *dep = VTODE(ap->a_vp);
332 struct msdosfsmount *pmp = dep->de_pmp;
333 struct vnode *vp = ap->a_vp;
334 struct vattr *vap = ap->a_vap;
335 kauth_cred_t cred = ap->a_cred;
336
337 #ifdef MSDOSFS_DEBUG
338 printf("msdosfs_setattr(): vp %p, vap %p, cred %p\n",
339 ap->a_vp, vap, cred);
340 #endif
341 /*
342 * Note we silently ignore uid or gid changes.
343 */
344 if ((vap->va_type != VNON) || (vap->va_nlink != (nlink_t)VNOVAL) ||
345 (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
346 (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
347 (vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL) ||
348 (vap->va_uid != VNOVAL && vap->va_uid != pmp->pm_uid) ||
349 (vap->va_gid != VNOVAL && vap->va_gid != pmp->pm_gid)) {
350 #ifdef MSDOSFS_DEBUG
351 printf("msdosfs_setattr(): returning EINVAL\n");
352 printf(" va_type %d, va_nlink %x, va_fsid %"PRIx64", va_fileid %llx\n",
353 vap->va_type, vap->va_nlink, vap->va_fsid,
354 (unsigned long long)vap->va_fileid);
355 printf(" va_blocksize %lx, va_rdev %"PRIx64", va_bytes %"PRIx64", va_gen %lx\n",
356 vap->va_blocksize, vap->va_rdev, vap->va_bytes, vap->va_gen);
357 #endif
358 return (EINVAL);
359 }
360 /*
361 * Silently ignore attributes modifications on directories.
362 */
363 if (ap->a_vp->v_type == VDIR)
364 return 0;
365
366 fstrans_start(vp->v_mount, FSTRANS_SHARED);
367 if (vap->va_size != VNOVAL) {
368 if (vp->v_mount->mnt_flag & MNT_RDONLY) {
369 error = EROFS;
370 goto bad;
371 }
372 error = detrunc(dep, (u_long)vap->va_size, 0, cred);
373 if (error)
374 goto bad;
375 de_changed = 1;
376 }
377 if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
378 if (vp->v_mount->mnt_flag & MNT_RDONLY) {
379 error = EROFS;
380 goto bad;
381 }
382 error = genfs_can_chtimes(ap->a_vp, vap->va_vaflags,
383 pmp->pm_uid, cred);
384 if (error)
385 goto bad;
386 if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95) == 0 &&
387 vap->va_atime.tv_sec != VNOVAL)
388 unix2dostime(&vap->va_atime, pmp->pm_gmtoff, &dep->de_ADate, NULL, NULL);
389 if (vap->va_mtime.tv_sec != VNOVAL)
390 unix2dostime(&vap->va_mtime, pmp->pm_gmtoff, &dep->de_MDate, &dep->de_MTime, NULL);
391 dep->de_Attributes |= ATTR_ARCHIVE;
392 dep->de_flag |= DE_MODIFIED;
393 de_changed = 1;
394 }
395 /*
396 * DOS files only have the ability to have their writability
397 * attribute set, so we use the owner write bit to set the readonly
398 * attribute.
399 */
400 if (vap->va_mode != (mode_t)VNOVAL) {
401 if (vp->v_mount->mnt_flag & MNT_RDONLY) {
402 error = EROFS;
403 goto bad;
404 }
405 if (kauth_cred_geteuid(cred) != pmp->pm_uid &&
406 (error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
407 NULL)))
408 goto bad;
409 /* We ignore the read and execute bits. */
410 if (vap->va_mode & S_IWUSR)
411 dep->de_Attributes &= ~ATTR_READONLY;
412 else
413 dep->de_Attributes |= ATTR_READONLY;
414 dep->de_flag |= DE_MODIFIED;
415 de_changed = 1;
416 }
417 /*
418 * Allow the `archived' bit to be toggled.
419 */
420 if (vap->va_flags != VNOVAL) {
421 if (vp->v_mount->mnt_flag & MNT_RDONLY) {
422 error = EROFS;
423 goto bad;
424 }
425 if (kauth_cred_geteuid(cred) != pmp->pm_uid &&
426 (error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
427 NULL)))
428 goto bad;
429 if (vap->va_flags & SF_ARCHIVED)
430 dep->de_Attributes &= ~ATTR_ARCHIVE;
431 else
432 dep->de_Attributes |= ATTR_ARCHIVE;
433 dep->de_flag |= DE_MODIFIED;
434 de_changed = 1;
435 }
436
437 if (de_changed) {
438 VN_KNOTE(vp, NOTE_ATTRIB);
439 error = deupdat(dep, 1);
440 if (error)
441 goto bad;
442 }
443
444 bad:
445 fstrans_done(vp->v_mount);
446 return error;
447 }
448
449 int
450 msdosfs_read(void *v)
451 {
452 struct vop_read_args /* {
453 struct vnode *a_vp;
454 struct uio *a_uio;
455 int a_ioflag;
456 kauth_cred_t a_cred;
457 } */ *ap = v;
458 int error = 0;
459 int64_t diff;
460 int blsize;
461 long n;
462 long on;
463 daddr_t lbn;
464 vsize_t bytelen;
465 struct buf *bp;
466 struct vnode *vp = ap->a_vp;
467 struct denode *dep = VTODE(vp);
468 struct msdosfsmount *pmp = dep->de_pmp;
469 struct uio *uio = ap->a_uio;
470
471 /*
472 * If they didn't ask for any data, then we are done.
473 */
474
475 if (uio->uio_resid == 0)
476 return (0);
477 if (uio->uio_offset < 0)
478 return (EINVAL);
479 if (uio->uio_offset >= dep->de_FileSize)
480 return (0);
481
482 fstrans_start(vp->v_mount, FSTRANS_SHARED);
483 if (vp->v_type == VREG) {
484 const int advice = IO_ADV_DECODE(ap->a_ioflag);
485
486 while (uio->uio_resid > 0) {
487 bytelen = MIN(dep->de_FileSize - uio->uio_offset,
488 uio->uio_resid);
489
490 if (bytelen == 0)
491 break;
492 error = ubc_uiomove(&vp->v_uobj, uio, bytelen, advice,
493 UBC_READ | UBC_PARTIALOK | UBC_UNMAP_FLAG(vp));
494 if (error)
495 break;
496 }
497 dep->de_flag |= DE_ACCESS;
498 goto out;
499 }
500
501 /* this loop is only for directories now */
502 do {
503 lbn = de_cluster(pmp, uio->uio_offset);
504 on = uio->uio_offset & pmp->pm_crbomask;
505 n = MIN(pmp->pm_bpcluster - on, uio->uio_resid);
506 if (uio->uio_offset >= dep->de_FileSize)
507 return (0);
508 /* file size (and hence diff) may be up to 4GB */
509 diff = dep->de_FileSize - uio->uio_offset;
510 if (diff < n)
511 n = (long) diff;
512
513 /* convert cluster # to sector # */
514 error = pcbmap(dep, lbn, &lbn, 0, &blsize);
515 if (error)
516 goto bad;
517
518 /*
519 * If we are operating on a directory file then be sure to
520 * do i/o with the vnode for the filesystem instead of the
521 * vnode for the directory.
522 */
523 error = bread(pmp->pm_devvp, de_bn2kb(pmp, lbn), blsize,
524 NOCRED, 0, &bp);
525 n = MIN(n, pmp->pm_bpcluster - bp->b_resid);
526 if (error) {
527 brelse(bp, 0);
528 goto bad;
529 }
530 error = uiomove((char *)bp->b_data + on, (int) n, uio);
531 brelse(bp, 0);
532 } while (error == 0 && uio->uio_resid > 0 && n != 0);
533
534 out:
535 if ((ap->a_ioflag & IO_SYNC) == IO_SYNC)
536 error = deupdat(dep, 1);
537 bad:
538 fstrans_done(vp->v_mount);
539 return (error);
540 }
541
542 /*
543 * Write data to a file or directory.
544 */
545 int
546 msdosfs_write(void *v)
547 {
548 struct vop_write_args /* {
549 struct vnode *a_vp;
550 struct uio *a_uio;
551 int a_ioflag;
552 kauth_cred_t a_cred;
553 } */ *ap = v;
554 int resid, extended = 0;
555 int error = 0;
556 int ioflag = ap->a_ioflag;
557 u_long osize;
558 u_long count;
559 vsize_t bytelen;
560 off_t oldoff;
561 struct uio *uio = ap->a_uio;
562 struct vnode *vp = ap->a_vp;
563 struct denode *dep = VTODE(vp);
564 struct msdosfsmount *pmp = dep->de_pmp;
565 kauth_cred_t cred = ap->a_cred;
566 bool async;
567
568 #ifdef MSDOSFS_DEBUG
569 printf("msdosfs_write(vp %p, uio %p, ioflag %x, cred %p\n",
570 vp, uio, ioflag, cred);
571 printf("msdosfs_write(): diroff %lu, dirclust %lu, startcluster %lu\n",
572 dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster);
573 #endif
574
575 switch (vp->v_type) {
576 case VREG:
577 if (ioflag & IO_APPEND)
578 uio->uio_offset = dep->de_FileSize;
579 break;
580 case VDIR:
581 return EISDIR;
582 default:
583 panic("msdosfs_write(): bad file type");
584 }
585
586 if (uio->uio_offset < 0)
587 return (EINVAL);
588
589 if (uio->uio_resid == 0)
590 return (0);
591
592 /* Don't bother to try to write files larger than the fs limit */
593 if (uio->uio_offset + uio->uio_resid > MSDOSFS_FILESIZE_MAX)
594 return (EFBIG);
595
596 fstrans_start(vp->v_mount, FSTRANS_SHARED);
597 /*
598 * If the offset we are starting the write at is beyond the end of
599 * the file, then they've done a seek. Unix filesystems allow
600 * files with holes in them, DOS doesn't so we must fill the hole
601 * with zeroed blocks.
602 */
603 if (uio->uio_offset > dep->de_FileSize) {
604 if ((error = deextend(dep, uio->uio_offset, cred)) != 0) {
605 fstrans_done(vp->v_mount);
606 return (error);
607 }
608 }
609
610 /*
611 * Remember some values in case the write fails.
612 */
613 async = vp->v_mount->mnt_flag & MNT_ASYNC;
614 resid = uio->uio_resid;
615 osize = dep->de_FileSize;
616
617 /*
618 * If we write beyond the end of the file, extend it to its ultimate
619 * size ahead of the time to hopefully get a contiguous area.
620 */
621 if (uio->uio_offset + resid > osize) {
622 count = de_clcount(pmp, uio->uio_offset + resid) -
623 de_clcount(pmp, osize);
624 if ((error = extendfile(dep, count, NULL, NULL, 0)))
625 goto errexit;
626
627 dep->de_FileSize = uio->uio_offset + resid;
628 /* hint uvm to not read in extended part */
629 uvm_vnp_setwritesize(vp, dep->de_FileSize);
630 extended = 1;
631 }
632
633 do {
634 oldoff = uio->uio_offset;
635 bytelen = uio->uio_resid;
636
637 error = ubc_uiomove(&vp->v_uobj, uio, bytelen,
638 IO_ADV_DECODE(ioflag), UBC_WRITE | UBC_UNMAP_FLAG(vp));
639 if (error)
640 break;
641
642 /*
643 * flush what we just wrote if necessary.
644 * XXXUBC simplistic async flushing.
645 */
646
647 if (!async && oldoff >> 16 != uio->uio_offset >> 16) {
648 mutex_enter(&vp->v_interlock);
649 error = VOP_PUTPAGES(vp, (oldoff >> 16) << 16,
650 (uio->uio_offset >> 16) << 16, PGO_CLEANIT);
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 mutex_enter(&vp->v_interlock);
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 (resid > uio->uio_resid)
669 VN_KNOTE(vp, NOTE_WRITE | (extended ? NOTE_EXTEND : 0));
670 if (error) {
671 detrunc(dep, osize, ioflag & IO_SYNC, NOCRED);
672 uio->uio_offset -= resid - uio->uio_resid;
673 uio->uio_resid = resid;
674 } else if ((ioflag & IO_SYNC) == IO_SYNC)
675 error = deupdat(dep, 1);
676 fstrans_done(vp->v_mount);
677 KASSERT(vp->v_size == dep->de_FileSize);
678 return (error);
679 }
680
681 int
682 msdosfs_update(struct vnode *vp, const struct timespec *acc,
683 const struct timespec *mod, int flags)
684 {
685 struct buf *bp;
686 struct direntry *dirp;
687 struct denode *dep;
688 int error;
689
690 if (vp->v_mount->mnt_flag & MNT_RDONLY)
691 return (0);
692 dep = VTODE(vp);
693 DETIMES(dep, acc, mod, NULL, dep->de_pmp->pm_gmtoff);
694 if ((dep->de_flag & DE_MODIFIED) == 0)
695 return (0);
696 dep->de_flag &= ~DE_MODIFIED;
697 if (dep->de_Attributes & ATTR_DIRECTORY)
698 return (0);
699 if (dep->de_refcnt <= 0)
700 return (0);
701 error = readde(dep, &bp, &dirp);
702 if (error)
703 return (error);
704 DE_EXTERNALIZE(dirp, dep);
705 if (flags & (UPDATE_WAIT|UPDATE_DIROP))
706 return (bwrite(bp));
707 else {
708 bdwrite(bp);
709 return (0);
710 }
711 }
712
713 /*
714 * Flush the blocks of a file to disk.
715 *
716 * This function is worthless for vnodes that represent directories. Maybe we
717 * could just do a sync if they try an fsync on a directory file.
718 */
719 int
720 msdosfs_remove(void *v)
721 {
722 struct vop_remove_args /* {
723 struct vnode *a_dvp;
724 struct vnode *a_vp;
725 struct componentname *a_cnp;
726 } */ *ap = v;
727 struct denode *dep = VTODE(ap->a_vp);
728 struct denode *ddep = VTODE(ap->a_dvp);
729 int error;
730
731 fstrans_start(ap->a_dvp->v_mount, FSTRANS_SHARED);
732 if (ap->a_vp->v_type == VDIR)
733 error = EPERM;
734 else
735 error = removede(ddep, dep);
736 #ifdef MSDOSFS_DEBUG
737 printf("msdosfs_remove(), dep %p, v_usecount %d\n",
738 dep, ap->a_vp->v_usecount);
739 #endif
740 VN_KNOTE(ap->a_vp, NOTE_DELETE);
741 VN_KNOTE(ap->a_dvp, NOTE_WRITE);
742 if (ddep == dep)
743 vrele(ap->a_vp);
744 else
745 vput(ap->a_vp); /* causes msdosfs_inactive() to be called
746 * via vrele() */
747 vput(ap->a_dvp);
748 fstrans_done(ap->a_dvp->v_mount);
749 return (error);
750 }
751
752 /*
753 * Renames on files require moving the denode to a new hash queue since the
754 * denode's location is used to compute which hash queue to put the file
755 * in. Unless it is a rename in place. For example "mv a b".
756 *
757 * What follows is the basic algorithm:
758 *
759 * if (file move) {
760 * if (dest file exists) {
761 * remove dest file
762 * }
763 * if (dest and src in same directory) {
764 * rewrite name in existing directory slot
765 * } else {
766 * write new entry in dest directory
767 * update offset and dirclust in denode
768 * move denode to new hash chain
769 * clear old directory entry
770 * }
771 * } else {
772 * directory move
773 * if (dest directory exists) {
774 * if (dest is not empty) {
775 * return ENOTEMPTY
776 * }
777 * remove dest directory
778 * }
779 * if (dest and src in same directory) {
780 * rewrite name in existing entry
781 * } else {
782 * be sure dest is not a child of src directory
783 * write entry in dest directory
784 * update "." and ".." in moved directory
785 * update offset and dirclust in denode
786 * move denode to new hash chain
787 * clear old directory entry for moved directory
788 * }
789 * }
790 *
791 * On entry:
792 * source's parent directory is unlocked
793 * source file or directory is unlocked
794 * destination's parent directory is locked
795 * destination file or directory is locked if it exists
796 *
797 * On exit:
798 * all denodes should be released
799 *
800 * Notes:
801 * I'm not sure how the memory containing the pathnames pointed at by the
802 * componentname structures is freed, there may be some memory bleeding
803 * for each rename done.
804 *
805 * --More-- Notes:
806 * This routine needs help. badly.
807 */
808 int
809 msdosfs_rename(void *v)
810 {
811 struct vop_rename_args /* {
812 struct vnode *a_fdvp;
813 struct vnode *a_fvp;
814 struct componentname *a_fcnp;
815 struct vnode *a_tdvp;
816 struct vnode *a_tvp;
817 struct componentname *a_tcnp;
818 } */ *ap = v;
819 struct vnode *tvp = ap->a_tvp;
820 struct vnode *tdvp = ap->a_tdvp;
821 struct vnode *fvp = ap->a_fvp;
822 struct vnode *fdvp = ap->a_fdvp;
823 struct componentname *tcnp = ap->a_tcnp;
824 struct componentname *fcnp = ap->a_fcnp;
825 struct denode *ip, *xp, *dp, *zp;
826 u_char toname[11], oldname[11];
827 u_long from_diroffset, to_diroffset;
828 u_char to_count;
829 int doingdirectory = 0, newparent = 0;
830 int error;
831 u_long cn;
832 daddr_t bn;
833 struct msdosfsmount *pmp;
834 struct direntry *dotdotp;
835 struct buf *bp;
836 int fdvp_dorele = 0;
837
838 pmp = VFSTOMSDOSFS(fdvp->v_mount);
839
840 #ifdef DIAGNOSTIC
841 if ((tcnp->cn_flags & HASBUF) == 0 ||
842 (fcnp->cn_flags & HASBUF) == 0)
843 panic("msdosfs_rename: no name");
844 #endif
845 /*
846 * Check for cross-device rename.
847 */
848 if ((fvp->v_mount != tdvp->v_mount) ||
849 (tvp && (fvp->v_mount != tvp->v_mount))) {
850 error = EXDEV;
851 abortit:
852 VOP_ABORTOP(tdvp, tcnp);
853 if (tdvp == tvp)
854 vrele(tdvp);
855 else
856 vput(tdvp);
857 if (tvp)
858 vput(tvp);
859 VOP_ABORTOP(fdvp, fcnp);
860 vrele(fdvp);
861 vrele(fvp);
862 return (error);
863 }
864
865 /*
866 * If source and dest are the same, do nothing.
867 */
868 if (tvp == fvp) {
869 error = 0;
870 goto abortit;
871 }
872
873 /*
874 * XXX: This can deadlock since we hold tdvp/tvp locked.
875 * But I'm not going to fix it now.
876 */
877 if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0)
878 goto abortit;
879 dp = VTODE(fdvp);
880 ip = VTODE(fvp);
881
882 /*
883 * Be sure we are not renaming ".", "..", or an alias of ".". This
884 * leads to a crippled directory tree. It's pretty tough to do a
885 * "ls" or "pwd" with the "." directory entry missing, and "cd .."
886 * doesn't work if the ".." entry is missing.
887 */
888 if (ip->de_Attributes & ATTR_DIRECTORY) {
889 /*
890 * Avoid ".", "..", and aliases of "." for obvious reasons.
891 */
892 if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
893 dp == ip ||
894 (fcnp->cn_flags & ISDOTDOT) ||
895 (tcnp->cn_flags & ISDOTDOT) ||
896 (ip->de_flag & DE_RENAME)) {
897 VOP_UNLOCK(fvp);
898 error = EINVAL;
899 goto abortit;
900 }
901 ip->de_flag |= DE_RENAME;
902 doingdirectory++;
903 }
904 VN_KNOTE(fdvp, NOTE_WRITE); /* XXXLUKEM/XXX: right place? */
905
906 fstrans_start(fdvp->v_mount, FSTRANS_SHARED);
907 /*
908 * When the target exists, both the directory
909 * and target vnodes are returned locked.
910 */
911 dp = VTODE(tdvp);
912 xp = tvp ? VTODE(tvp) : NULL;
913 /*
914 * Remember direntry place to use for destination
915 */
916 to_diroffset = dp->de_fndoffset;
917 to_count = dp->de_fndcnt;
918
919 /*
920 * If ".." must be changed (ie the directory gets a new
921 * parent) then the source directory must not be in the
922 * directory hierarchy above the target, as this would
923 * orphan everything below the source directory. Also
924 * the user must have write permission in the source so
925 * as to be able to change "..". We must repeat the call
926 * to namei, as the parent directory is unlocked by the
927 * call to doscheckpath().
928 */
929 error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred);
930 VOP_UNLOCK(fvp);
931 if (VTODE(fdvp)->de_StartCluster != VTODE(tdvp)->de_StartCluster)
932 newparent = 1;
933
934 /*
935 * XXX: We can do this here because rename uses SAVEFART and
936 * therefore fdvp has at least two references (one doesn't
937 * belong to us, though, and that's evil). We'll get
938 * another "extra" reference when we do relookup(), so we
939 * need to compensate. We should *NOT* be doing this, but
940 * it works, so whatever.
941 */
942 vrele(fdvp);
943
944 if (doingdirectory && newparent) {
945 if (error) /* write access check above */
946 goto tdvpbad;
947 if (xp != NULL)
948 vput(tvp);
949 tvp = NULL;
950 /*
951 * doscheckpath() vput()'s dp,
952 * so we have to do a relookup afterwards
953 */
954 if ((error = doscheckpath(ip, dp)) != 0)
955 goto out;
956 if ((tcnp->cn_flags & SAVESTART) == 0)
957 panic("msdosfs_rename: lost to startdir");
958 vn_lock(tdvp, LK_EXCLUSIVE | LK_RETRY);
959 if ((error = relookup(tdvp, &tvp, tcnp)) != 0) {
960 VOP_UNLOCK(tdvp);
961 goto out;
962 }
963 /*
964 * XXX: SAVESTART causes us to get a reference, but
965 * that's released already above in doscheckpath()
966 */
967 dp = VTODE(tdvp);
968 xp = tvp ? VTODE(tvp) : NULL;
969 }
970
971 if (xp != NULL) {
972 /*
973 * Target must be empty if a directory and have no links
974 * to it. Also, ensure source and target are compatible
975 * (both directories, or both not directories).
976 */
977 if (xp->de_Attributes & ATTR_DIRECTORY) {
978 if (!dosdirempty(xp)) {
979 error = ENOTEMPTY;
980 goto tdvpbad;
981 }
982 if (!doingdirectory) {
983 error = ENOTDIR;
984 goto tdvpbad;
985 }
986 } else if (doingdirectory) {
987 error = EISDIR;
988 goto tdvpbad;
989 }
990 if ((error = removede(dp, xp)) != 0)
991 goto tdvpbad;
992 VN_KNOTE(tdvp, NOTE_WRITE);
993 VN_KNOTE(tvp, NOTE_DELETE);
994 cache_purge(tvp);
995 vput(tvp);
996 tvp = NULL;
997 xp = NULL;
998 }
999
1000 /*
1001 * Convert the filename in tcnp into a dos filename. We copy this
1002 * into the denode and directory entry for the destination
1003 * file/directory.
1004 */
1005 if ((error = uniqdosname(VTODE(tdvp), tcnp, toname)) != 0) {
1006 fstrans_done(fdvp->v_mount);
1007 goto abortit;
1008 }
1009
1010 /*
1011 * Since from wasn't locked at various places above,
1012 * have to do a relookup here.
1013 */
1014 fcnp->cn_flags &= ~MODMASK;
1015 fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
1016 if ((fcnp->cn_flags & SAVESTART) == 0)
1017 panic("msdosfs_rename: lost from startdir");
1018 VOP_UNLOCK(tdvp);
1019 vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY);
1020 if ((error = relookup(fdvp, &fvp, fcnp))) {
1021 VOP_UNLOCK(fdvp);
1022 vrele(ap->a_fvp);
1023 vrele(tdvp);
1024 fstrans_done(fdvp->v_mount);
1025 return (error);
1026 }
1027 if (fvp == NULL) {
1028 /*
1029 * From name has disappeared.
1030 */
1031 if (doingdirectory)
1032 panic("rename: lost dir entry");
1033 vput(fdvp);
1034 vrele(ap->a_fvp);
1035 vrele(tdvp);
1036 fstrans_done(fdvp->v_mount);
1037 return 0;
1038 }
1039 fdvp_dorele = 1;
1040 VOP_UNLOCK(fdvp);
1041 xp = VTODE(fvp);
1042 zp = VTODE(fdvp);
1043 from_diroffset = zp->de_fndoffset;
1044
1045 /*
1046 * Ensure that the directory entry still exists and has not
1047 * changed till now. If the source is a file the entry may
1048 * have been unlinked or renamed. In either case there is
1049 * no further work to be done. If the source is a directory
1050 * then it cannot have been rmdir'ed or renamed; this is
1051 * prohibited by the DE_RENAME flag.
1052 */
1053 if (xp != ip) {
1054 if (doingdirectory)
1055 panic("rename: lost dir entry");
1056 vrele(ap->a_fvp);
1057 xp = NULL;
1058 } else {
1059 vrele(fvp);
1060 xp = NULL;
1061
1062 /*
1063 * First write a new entry in the destination
1064 * directory and mark the entry in the source directory
1065 * as deleted. Then move the denode to the correct hash
1066 * chain for its new location in the filesystem. And, if
1067 * we moved a directory, then update its .. entry to point
1068 * to the new parent directory.
1069 */
1070 memcpy(oldname, ip->de_Name, 11);
1071 memcpy(ip->de_Name, toname, 11); /* update denode */
1072 dp->de_fndoffset = to_diroffset;
1073 dp->de_fndcnt = to_count;
1074 error = createde(ip, dp, (struct denode **)0, tcnp);
1075 if (error) {
1076 memcpy(ip->de_Name, oldname, 11);
1077 VOP_UNLOCK(fvp);
1078 goto bad;
1079 }
1080 ip->de_refcnt++;
1081 zp->de_fndoffset = from_diroffset;
1082 if ((error = removede(zp, ip)) != 0) {
1083 /* XXX should really panic here, fs is corrupt */
1084 VOP_UNLOCK(fvp);
1085 goto bad;
1086 }
1087 cache_purge(fvp);
1088 if (!doingdirectory) {
1089 error = pcbmap(dp, de_cluster(pmp, to_diroffset), 0,
1090 &ip->de_dirclust, 0);
1091 if (error) {
1092 /* XXX should really panic here, fs is corrupt */
1093 VOP_UNLOCK(fvp);
1094 goto bad;
1095 }
1096 ip->de_diroffset = to_diroffset;
1097 if (ip->de_dirclust != MSDOSFSROOT)
1098 ip->de_diroffset &= pmp->pm_crbomask;
1099 }
1100 reinsert(ip);
1101 }
1102
1103 /*
1104 * If we moved a directory to a new parent directory, then we must
1105 * fixup the ".." entry in the moved directory.
1106 */
1107 if (doingdirectory && newparent) {
1108 cn = ip->de_StartCluster;
1109 if (cn == MSDOSFSROOT) {
1110 /* this should never happen */
1111 panic("msdosfs_rename: updating .. in root directory?");
1112 } else
1113 bn = cntobn(pmp, cn);
1114 error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn),
1115 pmp->pm_bpcluster, NOCRED, B_MODIFY, &bp);
1116 if (error) {
1117 /* XXX should really panic here, fs is corrupt */
1118 brelse(bp, 0);
1119 VOP_UNLOCK(fvp);
1120 goto bad;
1121 }
1122 dotdotp = (struct direntry *)bp->b_data + 1;
1123 putushort(dotdotp->deStartCluster, dp->de_StartCluster);
1124 if (FAT32(pmp)) {
1125 putushort(dotdotp->deHighClust,
1126 dp->de_StartCluster >> 16);
1127 } else {
1128 putushort(dotdotp->deHighClust, 0);
1129 }
1130 if ((error = bwrite(bp)) != 0) {
1131 /* XXX should really panic here, fs is corrupt */
1132 VOP_UNLOCK(fvp);
1133 goto bad;
1134 }
1135 }
1136
1137 VN_KNOTE(fvp, NOTE_RENAME);
1138 VOP_UNLOCK(fvp);
1139 bad:
1140 if (tvp)
1141 vput(tvp);
1142 vrele(tdvp);
1143 out:
1144 ip->de_flag &= ~DE_RENAME;
1145 if (fdvp_dorele)
1146 vrele(fdvp);
1147 vrele(fvp);
1148 fstrans_done(fdvp->v_mount);
1149 return (error);
1150
1151 /* XXX: uuuh */
1152 tdvpbad:
1153 VOP_UNLOCK(tdvp);
1154 goto bad;
1155 }
1156
1157 static const struct {
1158 struct direntry dot;
1159 struct direntry dotdot;
1160 } dosdirtemplate = {
1161 { ". ", " ", /* the . entry */
1162 ATTR_DIRECTORY, /* file attribute */
1163 0, /* reserved */
1164 0, { 0, 0 }, { 0, 0 }, /* create time & date */
1165 { 0, 0 }, /* access date */
1166 { 0, 0 }, /* high bits of start cluster */
1167 { 210, 4 }, { 210, 4 }, /* modify time & date */
1168 { 0, 0 }, /* startcluster */
1169 { 0, 0, 0, 0 } /* filesize */
1170 },
1171 { ".. ", " ", /* the .. entry */
1172 ATTR_DIRECTORY, /* file attribute */
1173 0, /* reserved */
1174 0, { 0, 0 }, { 0, 0 }, /* create time & date */
1175 { 0, 0 }, /* access date */
1176 { 0, 0 }, /* high bits of start cluster */
1177 { 210, 4 }, { 210, 4 }, /* modify time & date */
1178 { 0, 0 }, /* startcluster */
1179 { 0, 0, 0, 0 } /* filesize */
1180 }
1181 };
1182
1183 int
1184 msdosfs_mkdir(void *v)
1185 {
1186 struct vop_mkdir_args /* {
1187 struct vnode *a_dvp;
1188 struvt vnode **a_vpp;
1189 struvt componentname *a_cnp;
1190 struct vattr *a_vap;
1191 } */ *ap = v;
1192 struct componentname *cnp = ap->a_cnp;
1193 struct denode ndirent;
1194 struct denode *dep;
1195 struct denode *pdep = VTODE(ap->a_dvp);
1196 int error;
1197 int bn;
1198 u_long newcluster, pcl;
1199 daddr_t lbn;
1200 struct direntry *denp;
1201 struct msdosfsmount *pmp = pdep->de_pmp;
1202 struct buf *bp;
1203 int async = pdep->de_pmp->pm_mountp->mnt_flag & MNT_ASYNC;
1204
1205 fstrans_start(ap->a_dvp->v_mount, FSTRANS_SHARED);
1206 /*
1207 * If this is the root directory and there is no space left we
1208 * can't do anything. This is because the root directory can not
1209 * change size.
1210 */
1211 if (pdep->de_StartCluster == MSDOSFSROOT
1212 && pdep->de_fndoffset >= pdep->de_FileSize) {
1213 error = ENOSPC;
1214 goto bad2;
1215 }
1216
1217 /*
1218 * Allocate a cluster to hold the about to be created directory.
1219 */
1220 error = clusteralloc(pmp, 0, 1, &newcluster, NULL);
1221 if (error)
1222 goto bad2;
1223
1224 memset(&ndirent, 0, sizeof(ndirent));
1225 ndirent.de_pmp = pmp;
1226 ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
1227 DETIMES(&ndirent, NULL, NULL, NULL, pmp->pm_gmtoff);
1228
1229 /*
1230 * Now fill the cluster with the "." and ".." entries. And write
1231 * the cluster to disk. This way it is there for the parent
1232 * directory to be pointing at if there were a crash.
1233 */
1234 bn = cntobn(pmp, newcluster);
1235 lbn = de_bn2kb(pmp, bn);
1236 /* always succeeds */
1237 bp = getblk(pmp->pm_devvp, lbn, pmp->pm_bpcluster, 0, 0);
1238 memset(bp->b_data, 0, pmp->pm_bpcluster);
1239 memcpy(bp->b_data, &dosdirtemplate, sizeof dosdirtemplate);
1240 denp = (struct direntry *)bp->b_data;
1241 putushort(denp[0].deStartCluster, newcluster);
1242 putushort(denp[0].deCDate, ndirent.de_CDate);
1243 putushort(denp[0].deCTime, ndirent.de_CTime);
1244 denp[0].deCHundredth = ndirent.de_CHun;
1245 putushort(denp[0].deADate, ndirent.de_ADate);
1246 putushort(denp[0].deMDate, ndirent.de_MDate);
1247 putushort(denp[0].deMTime, ndirent.de_MTime);
1248 pcl = pdep->de_StartCluster;
1249 if (FAT32(pmp) && pcl == pmp->pm_rootdirblk)
1250 pcl = 0;
1251 putushort(denp[1].deStartCluster, pcl);
1252 putushort(denp[1].deCDate, ndirent.de_CDate);
1253 putushort(denp[1].deCTime, ndirent.de_CTime);
1254 denp[1].deCHundredth = ndirent.de_CHun;
1255 putushort(denp[1].deADate, ndirent.de_ADate);
1256 putushort(denp[1].deMDate, ndirent.de_MDate);
1257 putushort(denp[1].deMTime, ndirent.de_MTime);
1258 if (FAT32(pmp)) {
1259 putushort(denp[0].deHighClust, newcluster >> 16);
1260 putushort(denp[1].deHighClust, pdep->de_StartCluster >> 16);
1261 } else {
1262 putushort(denp[0].deHighClust, 0);
1263 putushort(denp[1].deHighClust, 0);
1264 }
1265
1266 if (async)
1267 bdwrite(bp);
1268 else if ((error = bwrite(bp)) != 0)
1269 goto bad;
1270
1271 /*
1272 * Now build up a directory entry pointing to the newly allocated
1273 * cluster. This will be written to an empty slot in the parent
1274 * directory.
1275 */
1276 #ifdef DIAGNOSTIC
1277 if ((cnp->cn_flags & HASBUF) == 0)
1278 panic("msdosfs_mkdir: no name");
1279 #endif
1280 if ((error = uniqdosname(pdep, cnp, ndirent.de_Name)) != 0)
1281 goto bad;
1282
1283 ndirent.de_Attributes = ATTR_DIRECTORY;
1284 ndirent.de_StartCluster = newcluster;
1285 ndirent.de_FileSize = 0;
1286 ndirent.de_dev = pdep->de_dev;
1287 ndirent.de_devvp = pdep->de_devvp;
1288 if ((error = createde(&ndirent, pdep, &dep, cnp)) != 0)
1289 goto bad;
1290 VN_KNOTE(ap->a_dvp, NOTE_WRITE | NOTE_LINK);
1291 vput(ap->a_dvp);
1292 *ap->a_vpp = DETOV(dep);
1293 fstrans_done(ap->a_dvp->v_mount);
1294 return (0);
1295
1296 bad:
1297 clusterfree(pmp, newcluster, NULL);
1298 bad2:
1299 vput(ap->a_dvp);
1300 fstrans_done(ap->a_dvp->v_mount);
1301 return (error);
1302 }
1303
1304 int
1305 msdosfs_rmdir(void *v)
1306 {
1307 struct vop_rmdir_args /* {
1308 struct vnode *a_dvp;
1309 struct vnode *a_vp;
1310 struct componentname *a_cnp;
1311 } */ *ap = v;
1312 struct vnode *vp = ap->a_vp;
1313 struct vnode *dvp = ap->a_dvp;
1314 struct componentname *cnp = ap->a_cnp;
1315 struct denode *ip, *dp;
1316 int error;
1317
1318 ip = VTODE(vp);
1319 dp = VTODE(dvp);
1320 /*
1321 * No rmdir "." please.
1322 */
1323 if (dp == ip) {
1324 vrele(dvp);
1325 vput(vp);
1326 return (EINVAL);
1327 }
1328 fstrans_start(ap->a_dvp->v_mount, FSTRANS_SHARED);
1329 /*
1330 * Verify the directory is empty (and valid).
1331 * (Rmdir ".." won't be valid since
1332 * ".." will contain a reference to
1333 * the current directory and thus be
1334 * non-empty.)
1335 */
1336 error = 0;
1337 if (!dosdirempty(ip) || ip->de_flag & DE_RENAME) {
1338 error = ENOTEMPTY;
1339 goto out;
1340 }
1341 /*
1342 * Delete the entry from the directory. For dos filesystems this
1343 * gets rid of the directory entry on disk, the in memory copy
1344 * still exists but the de_refcnt is <= 0. This prevents it from
1345 * being found by deget(). When the vput() on dep is done we give
1346 * up access and eventually msdosfs_reclaim() will be called which
1347 * will remove it from the denode cache.
1348 */
1349 if ((error = removede(dp, ip)) != 0)
1350 goto out;
1351 /*
1352 * This is where we decrement the link count in the parent
1353 * directory. Since dos filesystems don't do this we just purge
1354 * the name cache and let go of the parent directory denode.
1355 */
1356 VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
1357 cache_purge(dvp);
1358 vput(dvp);
1359 dvp = NULL;
1360 /*
1361 * Truncate the directory that is being deleted.
1362 */
1363 error = detrunc(ip, (u_long)0, IO_SYNC, cnp->cn_cred);
1364 cache_purge(vp);
1365 out:
1366 VN_KNOTE(vp, NOTE_DELETE);
1367 if (dvp)
1368 vput(dvp);
1369 vput(vp);
1370 fstrans_done(ap->a_dvp->v_mount);
1371 return (error);
1372 }
1373
1374 int
1375 msdosfs_readdir(void *v)
1376 {
1377 struct vop_readdir_args /* {
1378 struct vnode *a_vp;
1379 struct uio *a_uio;
1380 kauth_cred_t a_cred;
1381 int *a_eofflag;
1382 off_t **a_cookies;
1383 int *a_ncookies;
1384 } */ *ap = v;
1385 int error = 0;
1386 int diff;
1387 long n;
1388 int blsize;
1389 long on;
1390 long lost;
1391 long count;
1392 u_long cn;
1393 ino_t fileno;
1394 u_long dirsperblk;
1395 long bias = 0;
1396 daddr_t bn, lbn;
1397 struct buf *bp;
1398 struct denode *dep = VTODE(ap->a_vp);
1399 struct msdosfsmount *pmp = dep->de_pmp;
1400 struct direntry *dentp;
1401 struct dirent *dirbuf;
1402 struct uio *uio = ap->a_uio;
1403 off_t *cookies = NULL;
1404 int ncookies = 0, nc = 0;
1405 off_t offset, uio_off;
1406 int chksum = -1;
1407
1408 #ifdef MSDOSFS_DEBUG
1409 printf("msdosfs_readdir(): vp %p, uio %p, cred %p, eofflagp %p\n",
1410 ap->a_vp, uio, ap->a_cred, ap->a_eofflag);
1411 #endif
1412
1413 /*
1414 * msdosfs_readdir() won't operate properly on regular files since
1415 * it does i/o only with the filesystem vnode, and hence can
1416 * retrieve the wrong block from the buffer cache for a plain file.
1417 * So, fail attempts to readdir() on a plain file.
1418 */
1419 if ((dep->de_Attributes & ATTR_DIRECTORY) == 0)
1420 return (ENOTDIR);
1421
1422 /*
1423 * If the user buffer is smaller than the size of one dos directory
1424 * entry or the file offset is not a multiple of the size of a
1425 * directory entry, then we fail the read.
1426 */
1427 count = uio->uio_resid & ~(sizeof(struct direntry) - 1);
1428 offset = uio->uio_offset;
1429 if (count < sizeof(struct direntry) ||
1430 (offset & (sizeof(struct direntry) - 1)))
1431 return (EINVAL);
1432 lost = uio->uio_resid - count;
1433 uio->uio_resid = count;
1434 uio_off = uio->uio_offset;
1435
1436 fstrans_start(ap->a_vp->v_mount, FSTRANS_SHARED);
1437
1438 /* Allocate a temporary dirent buffer. */
1439 dirbuf = malloc(sizeof(struct dirent), M_MSDOSFSTMP, M_WAITOK | M_ZERO);
1440
1441 if (ap->a_ncookies) {
1442 nc = uio->uio_resid / _DIRENT_MINSIZE((struct dirent *)0);
1443 cookies = malloc(nc * sizeof (off_t), M_TEMP, M_WAITOK);
1444 *ap->a_cookies = cookies;
1445 }
1446
1447 dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry);
1448
1449 /*
1450 * If they are reading from the root directory then, we simulate
1451 * the . and .. entries since these don't exist in the root
1452 * directory. We also set the offset bias to make up for having to
1453 * simulate these entries. By this I mean that at file offset 64 we
1454 * read the first entry in the root directory that lives on disk.
1455 */
1456 if (dep->de_StartCluster == MSDOSFSROOT
1457 || (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk)) {
1458 #if 0
1459 printf("msdosfs_readdir(): going after . or .. in root dir, "
1460 "offset %" PRIu64 "\n", offset);
1461 #endif
1462 bias = 2 * sizeof(struct direntry);
1463 if (offset < bias) {
1464 for (n = (int)offset / sizeof(struct direntry);
1465 n < 2; n++) {
1466 if (FAT32(pmp))
1467 dirbuf->d_fileno = cntobn(pmp,
1468 (ino_t)pmp->pm_rootdirblk)
1469 * dirsperblk;
1470 else
1471 dirbuf->d_fileno = 1;
1472 dirbuf->d_type = DT_DIR;
1473 switch (n) {
1474 case 0:
1475 dirbuf->d_namlen = 1;
1476 strlcpy(dirbuf->d_name, ".",
1477 sizeof(dirbuf->d_name));
1478 break;
1479 case 1:
1480 dirbuf->d_namlen = 2;
1481 strlcpy(dirbuf->d_name, "..",
1482 sizeof(dirbuf->d_name));
1483 break;
1484 }
1485 dirbuf->d_reclen = _DIRENT_SIZE(dirbuf);
1486 if (uio->uio_resid < dirbuf->d_reclen)
1487 goto out;
1488 error = uiomove(dirbuf, dirbuf->d_reclen, uio);
1489 if (error)
1490 goto out;
1491 offset += sizeof(struct direntry);
1492 uio_off = offset;
1493 if (cookies) {
1494 *cookies++ = offset;
1495 ncookies++;
1496 if (ncookies >= nc)
1497 goto out;
1498 }
1499 }
1500 }
1501 }
1502
1503 while (uio->uio_resid > 0) {
1504 lbn = de_cluster(pmp, offset - bias);
1505 on = (offset - bias) & pmp->pm_crbomask;
1506 n = MIN(pmp->pm_bpcluster - on, uio->uio_resid);
1507 diff = dep->de_FileSize - (offset - bias);
1508 if (diff <= 0)
1509 break;
1510 n = MIN(n, diff);
1511 if ((error = pcbmap(dep, lbn, &bn, &cn, &blsize)) != 0)
1512 break;
1513 error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
1514 NOCRED, 0, &bp);
1515 if (error) {
1516 brelse(bp, 0);
1517 goto bad;
1518 }
1519 n = MIN(n, blsize - bp->b_resid);
1520
1521 /*
1522 * Convert from dos directory entries to fs-independent
1523 * directory entries.
1524 */
1525 for (dentp = (struct direntry *)((char *)bp->b_data + on);
1526 (char *)dentp < (char *)bp->b_data + on + n;
1527 dentp++, offset += sizeof(struct direntry)) {
1528 #if 0
1529
1530 printf("rd: dentp %08x prev %08x crnt %08x deName %02x attr %02x\n",
1531 dentp, prev, crnt, dentp->deName[0], dentp->deAttributes);
1532 #endif
1533 /*
1534 * If this is an unused entry, we can stop.
1535 */
1536 if (dentp->deName[0] == SLOT_EMPTY) {
1537 brelse(bp, 0);
1538 goto out;
1539 }
1540 /*
1541 * Skip deleted entries.
1542 */
1543 if (dentp->deName[0] == SLOT_DELETED) {
1544 chksum = -1;
1545 continue;
1546 }
1547
1548 /*
1549 * Handle Win95 long directory entries
1550 */
1551 if (dentp->deAttributes == ATTR_WIN95) {
1552 if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
1553 continue;
1554 chksum = win2unixfn((struct winentry *)dentp,
1555 dirbuf, chksum);
1556 continue;
1557 }
1558
1559 /*
1560 * Skip volume labels
1561 */
1562 if (dentp->deAttributes & ATTR_VOLUME) {
1563 chksum = -1;
1564 continue;
1565 }
1566 /*
1567 * This computation of d_fileno must match
1568 * the computation of va_fileid in
1569 * msdosfs_getattr.
1570 */
1571 if (dentp->deAttributes & ATTR_DIRECTORY) {
1572 fileno = getushort(dentp->deStartCluster);
1573 if (FAT32(pmp))
1574 fileno |= ((ino_t)getushort(dentp->deHighClust)) << 16;
1575 /* if this is the root directory */
1576 if (fileno == MSDOSFSROOT)
1577 if (FAT32(pmp))
1578 fileno = cntobn(pmp,
1579 (ino_t)pmp->pm_rootdirblk)
1580 * dirsperblk;
1581 else
1582 fileno = 1;
1583 else
1584 fileno = cntobn(pmp, fileno) * dirsperblk;
1585 dirbuf->d_fileno = fileno;
1586 dirbuf->d_type = DT_DIR;
1587 } else {
1588 dirbuf->d_fileno =
1589 offset / sizeof(struct direntry);
1590 dirbuf->d_type = DT_REG;
1591 }
1592 if (chksum != winChksum(dentp->deName))
1593 dirbuf->d_namlen = dos2unixfn(dentp->deName,
1594 (u_char *)dirbuf->d_name,
1595 pmp->pm_flags & MSDOSFSMNT_SHORTNAME);
1596 else
1597 dirbuf->d_name[dirbuf->d_namlen] = 0;
1598 chksum = -1;
1599 dirbuf->d_reclen = _DIRENT_SIZE(dirbuf);
1600 if (uio->uio_resid < dirbuf->d_reclen) {
1601 brelse(bp, 0);
1602 goto out;
1603 }
1604 error = uiomove(dirbuf, dirbuf->d_reclen, uio);
1605 if (error) {
1606 brelse(bp, 0);
1607 goto out;
1608 }
1609 uio_off = offset + sizeof(struct direntry);
1610 if (cookies) {
1611 *cookies++ = offset + sizeof(struct direntry);
1612 ncookies++;
1613 if (ncookies >= nc) {
1614 brelse(bp, 0);
1615 goto out;
1616 }
1617 }
1618 }
1619 brelse(bp, 0);
1620 }
1621
1622 out:
1623 uio->uio_offset = uio_off;
1624 uio->uio_resid += lost;
1625 if (dep->de_FileSize - (offset - bias) <= 0)
1626 *ap->a_eofflag = 1;
1627 else
1628 *ap->a_eofflag = 0;
1629
1630 if (ap->a_ncookies) {
1631 if (error) {
1632 free(*ap->a_cookies, M_TEMP);
1633 *ap->a_ncookies = 0;
1634 *ap->a_cookies = NULL;
1635 } else
1636 *ap->a_ncookies = ncookies;
1637 }
1638
1639 bad:
1640 free(dirbuf, M_MSDOSFSTMP);
1641 fstrans_done(ap->a_vp->v_mount);
1642 return (error);
1643 }
1644
1645 /*
1646 * vp - address of vnode file the file
1647 * bn - which cluster we are interested in mapping to a filesystem block number.
1648 * vpp - returns the vnode for the block special file holding the filesystem
1649 * containing the file of interest
1650 * bnp - address of where to return the filesystem relative block number
1651 */
1652 int
1653 msdosfs_bmap(void *v)
1654 {
1655 struct vop_bmap_args /* {
1656 struct vnode *a_vp;
1657 daddr_t a_bn;
1658 struct vnode **a_vpp;
1659 daddr_t *a_bnp;
1660 int *a_runp;
1661 } */ *ap = v;
1662 struct denode *dep = VTODE(ap->a_vp);
1663 int run, maxrun;
1664 daddr_t runbn;
1665 int status;
1666
1667 if (ap->a_vpp != NULL)
1668 *ap->a_vpp = dep->de_devvp;
1669 if (ap->a_bnp == NULL)
1670 return (0);
1671 status = pcbmap(dep, ap->a_bn, ap->a_bnp, 0, 0);
1672
1673 /*
1674 * From FreeBSD:
1675 * A little kludgy, but we loop calling pcbmap until we
1676 * reach the end of the contiguous piece, or reach MAXPHYS.
1677 * Since it reduces disk I/Os, the "wasted" CPU is put to
1678 * good use (4 to 5 fold sequential read I/O improvement on USB
1679 * drives).
1680 */
1681 if (ap->a_runp != NULL) {
1682 /* taken from ufs_bmap */
1683 maxrun = ulmin(MAXPHYS / dep->de_pmp->pm_bpcluster - 1,
1684 dep->de_pmp->pm_maxcluster - ap->a_bn);
1685 for (run = 1; run <= maxrun; run++) {
1686 if (pcbmap(dep, ap->a_bn + run, &runbn, NULL, NULL)
1687 != 0 || runbn !=
1688 *ap->a_bnp + de_cn2bn(dep->de_pmp, run))
1689 break;
1690 }
1691 *ap->a_runp = run - 1;
1692 }
1693
1694 /*
1695 * We need to scale *ap->a_bnp by sector_size/DEV_BSIZE
1696 */
1697 *ap->a_bnp = de_bn2kb(dep->de_pmp, *ap->a_bnp);
1698 return status;
1699 }
1700
1701 int
1702 msdosfs_strategy(void *v)
1703 {
1704 struct vop_strategy_args /* {
1705 struct vnode *a_vp;
1706 struct buf *a_bp;
1707 } */ *ap = v;
1708 struct vnode *vp = ap->a_vp;
1709 struct buf *bp = ap->a_bp;
1710 struct denode *dep = VTODE(bp->b_vp);
1711 int error = 0;
1712
1713 if (vp->v_type == VBLK || vp->v_type == VCHR)
1714 panic("msdosfs_strategy: spec");
1715 /*
1716 * If we don't already know the filesystem relative block number
1717 * then get it using pcbmap(). If pcbmap() returns the block
1718 * number as -1 then we've got a hole in the file. DOS filesystems
1719 * don't allow files with holes, so we shouldn't ever see this.
1720 */
1721 if (bp->b_blkno == bp->b_lblkno) {
1722 error = pcbmap(dep, de_bn2cn(dep->de_pmp, bp->b_lblkno),
1723 &bp->b_blkno, 0, 0);
1724 if (error)
1725 bp->b_blkno = -1;
1726 if (bp->b_blkno == -1)
1727 clrbuf(bp);
1728 else
1729 bp->b_blkno = de_bn2kb(dep->de_pmp, bp->b_blkno);
1730 }
1731 if (bp->b_blkno == -1) {
1732 biodone(bp);
1733 return (error);
1734 }
1735
1736 /*
1737 * Read/write the block from/to the disk that contains the desired
1738 * file block.
1739 */
1740
1741 vp = dep->de_devvp;
1742 return (VOP_STRATEGY(vp, bp));
1743 }
1744
1745 int
1746 msdosfs_print(void *v)
1747 {
1748 struct vop_print_args /* {
1749 struct vnode *vp;
1750 } */ *ap = v;
1751 struct denode *dep = VTODE(ap->a_vp);
1752
1753 printf(
1754 "tag VT_MSDOSFS, startcluster %ld, dircluster %ld, diroffset %ld ",
1755 dep->de_StartCluster, dep->de_dirclust, dep->de_diroffset);
1756 printf(" dev %llu, %llu ", (unsigned long long)major(dep->de_dev),
1757 (unsigned long long)minor(dep->de_dev));
1758 printf("\n");
1759 return (0);
1760 }
1761
1762 int
1763 msdosfs_advlock(void *v)
1764 {
1765 struct vop_advlock_args /* {
1766 struct vnode *a_vp;
1767 void *a_id;
1768 int a_op;
1769 struct flock *a_fl;
1770 int a_flags;
1771 } */ *ap = v;
1772 struct denode *dep = VTODE(ap->a_vp);
1773
1774 return lf_advlock(ap, &dep->de_lockf, dep->de_FileSize);
1775 }
1776
1777 int
1778 msdosfs_pathconf(void *v)
1779 {
1780 struct vop_pathconf_args /* {
1781 struct vnode *a_vp;
1782 int a_name;
1783 register_t *a_retval;
1784 } */ *ap = v;
1785
1786 switch (ap->a_name) {
1787 case _PC_LINK_MAX:
1788 *ap->a_retval = 1;
1789 return (0);
1790 case _PC_NAME_MAX:
1791 *ap->a_retval = ap->a_vp->v_mount->mnt_stat.f_namemax;
1792 return (0);
1793 case _PC_PATH_MAX:
1794 *ap->a_retval = PATH_MAX;
1795 return (0);
1796 case _PC_CHOWN_RESTRICTED:
1797 *ap->a_retval = 1;
1798 return (0);
1799 case _PC_NO_TRUNC:
1800 *ap->a_retval = 0;
1801 return (0);
1802 case _PC_SYNC_IO:
1803 *ap->a_retval = 1;
1804 return (0);
1805 case _PC_FILESIZEBITS:
1806 *ap->a_retval = 32;
1807 return (0);
1808 default:
1809 return (EINVAL);
1810 }
1811 /* NOTREACHED */
1812 }
1813
1814 int
1815 msdosfs_fsync(void *v)
1816 {
1817 struct vop_fsync_args /* {
1818 struct vnode *a_vp;
1819 kauth_cred_t a_cred;
1820 int a_flags;
1821 off_t offlo;
1822 off_t offhi;
1823 } */ *ap = v;
1824 struct vnode *vp = ap->a_vp;
1825 int wait;
1826 int error;
1827
1828 fstrans_start(vp->v_mount, FSTRANS_LAZY);
1829 wait = (ap->a_flags & FSYNC_WAIT) != 0;
1830 vflushbuf(vp, wait);
1831 if ((ap->a_flags & FSYNC_DATAONLY) != 0)
1832 error = 0;
1833 else
1834 error = msdosfs_update(vp, NULL, NULL, wait ? UPDATE_WAIT : 0);
1835
1836 if (error == 0 && ap->a_flags & FSYNC_CACHE) {
1837 struct denode *dep = VTODE(vp);
1838 struct vnode *devvp = dep->de_devvp;
1839
1840 int l = 0;
1841 error = VOP_IOCTL(devvp, DIOCCACHESYNC, &l, FWRITE,
1842 curlwp->l_cred);
1843 }
1844 fstrans_done(vp->v_mount);
1845
1846 return (error);
1847 }
1848
1849 void
1850 msdosfs_detimes(struct denode *dep, const struct timespec *acc,
1851 const struct timespec *mod, const struct timespec *cre, int gmtoff)
1852 {
1853 struct timespec *ts = NULL, tsb;
1854
1855 KASSERT(dep->de_flag & (DE_UPDATE | DE_CREATE | DE_ACCESS));
1856 /* XXX just call getnanotime early and use result if needed? */
1857 dep->de_flag |= DE_MODIFIED;
1858 if (dep->de_flag & DE_UPDATE) {
1859 if (mod == NULL) {
1860 getnanotime(&tsb);
1861 mod = ts = &tsb;
1862 }
1863 unix2dostime(mod, gmtoff, &dep->de_MDate, &dep->de_MTime, NULL);
1864 dep->de_Attributes |= ATTR_ARCHIVE;
1865 }
1866 if ((dep->de_pmp->pm_flags & MSDOSFSMNT_NOWIN95) == 0) {
1867 if (dep->de_flag & DE_ACCESS) {
1868 if (acc == NULL)
1869 acc = ts == NULL ?
1870 (getnanotime(&tsb), ts = &tsb) : ts;
1871 unix2dostime(acc, gmtoff, &dep->de_ADate, NULL, NULL);
1872 }
1873 if (dep->de_flag & DE_CREATE) {
1874 if (cre == NULL)
1875 cre = ts == NULL ?
1876 (getnanotime(&tsb), ts = &tsb) : ts;
1877 unix2dostime(cre, gmtoff, &dep->de_CDate,
1878 &dep->de_CTime, &dep->de_CHun);
1879 }
1880 }
1881
1882 dep->de_flag &= ~(DE_UPDATE | DE_CREATE | DE_ACCESS);
1883 }
1884
1885 /* Global vfs data structures for msdosfs */
1886 int (**msdosfs_vnodeop_p)(void *);
1887 const struct vnodeopv_entry_desc msdosfs_vnodeop_entries[] = {
1888 { &vop_default_desc, vn_default_error },
1889 { &vop_lookup_desc, msdosfs_lookup }, /* lookup */
1890 { &vop_create_desc, msdosfs_create }, /* create */
1891 { &vop_mknod_desc, genfs_eopnotsupp }, /* mknod */
1892 { &vop_open_desc, genfs_nullop }, /* open */
1893 { &vop_close_desc, msdosfs_close }, /* close */
1894 { &vop_access_desc, msdosfs_access }, /* access */
1895 { &vop_getattr_desc, msdosfs_getattr }, /* getattr */
1896 { &vop_setattr_desc, msdosfs_setattr }, /* setattr */
1897 { &vop_read_desc, msdosfs_read }, /* read */
1898 { &vop_write_desc, msdosfs_write }, /* write */
1899 { &vop_fcntl_desc, genfs_fcntl }, /* fcntl */
1900 { &vop_ioctl_desc, msdosfs_ioctl }, /* ioctl */
1901 { &vop_poll_desc, msdosfs_poll }, /* poll */
1902 { &vop_kqfilter_desc, genfs_kqfilter }, /* kqfilter */
1903 { &vop_revoke_desc, msdosfs_revoke }, /* revoke */
1904 { &vop_mmap_desc, msdosfs_mmap }, /* mmap */
1905 { &vop_fsync_desc, msdosfs_fsync }, /* fsync */
1906 { &vop_seek_desc, msdosfs_seek }, /* seek */
1907 { &vop_remove_desc, msdosfs_remove }, /* remove */
1908 { &vop_link_desc, genfs_eopnotsupp }, /* link */
1909 { &vop_rename_desc, msdosfs_rename }, /* rename */
1910 { &vop_mkdir_desc, msdosfs_mkdir }, /* mkdir */
1911 { &vop_rmdir_desc, msdosfs_rmdir }, /* rmdir */
1912 { &vop_symlink_desc, genfs_eopnotsupp }, /* symlink */
1913 { &vop_readdir_desc, msdosfs_readdir }, /* readdir */
1914 { &vop_readlink_desc, genfs_einval }, /* readlink */
1915 { &vop_abortop_desc, msdosfs_abortop }, /* abortop */
1916 { &vop_inactive_desc, msdosfs_inactive }, /* inactive */
1917 { &vop_reclaim_desc, msdosfs_reclaim }, /* reclaim */
1918 { &vop_lock_desc, genfs_lock }, /* lock */
1919 { &vop_unlock_desc, genfs_unlock }, /* unlock */
1920 { &vop_bmap_desc, msdosfs_bmap }, /* bmap */
1921 { &vop_strategy_desc, msdosfs_strategy }, /* strategy */
1922 { &vop_print_desc, msdosfs_print }, /* print */
1923 { &vop_islocked_desc, genfs_islocked }, /* islocked */
1924 { &vop_pathconf_desc, msdosfs_pathconf }, /* pathconf */
1925 { &vop_advlock_desc, msdosfs_advlock }, /* advlock */
1926 { &vop_bwrite_desc, vn_bwrite }, /* bwrite */
1927 { &vop_getpages_desc, genfs_getpages }, /* getpages */
1928 { &vop_putpages_desc, genfs_putpages }, /* putpages */
1929 { NULL, NULL }
1930 };
1931 const struct vnodeopv_desc msdosfs_vnodeop_opv_desc =
1932 { &msdosfs_vnodeop_p, msdosfs_vnodeop_entries };
1933