1 /* $NetBSD: msdosfs_vnops.c,v 1.16 2015/03/29 05:52:59 agc Exp $ */
4 * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
5 * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
7 * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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.
35 * Written by Paul Popelka (paulp@uts.amdahl.com)
37 * You can do anything you want with this software, just don't say you wrote
38 * it, and don't remove this notice.
40 * This software is provided "as is".
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.
49 #if HAVE_NBTOOL_CONFIG_H
50 #include "nbtool_config.h"
53 #include <sys/cdefs.h>
54 __KERNEL_RCSID(0, "$NetBSD: msdosfs_vnops.c,v 1.16 2015/03/29 05:52:59 agc Exp $");
56 #include <sys/param.h>
63 #include <fs/msdosfs/bpb.h>
64 #include <fs/msdosfs/direntry.h>
65 #include <fs/msdosfs/denode.h>
66 #include <fs/msdosfs/msdosfsmount.h>
67 #include <fs/msdosfs/fat.h>
73 #define DPRINTF(a) printf a
80 * In the ufs filesystem the inodes, superblocks, and indirect blocks are
81 * read/written using the vnode for the filesystem. Blocks that represent
82 * the contents of a file are read/written using the vnode for the file
83 * (including directories when they are read/written as files). This
84 * presents problems for the dos filesystem because data that should be in
85 * an inode (if dos had them) resides in the directory itself. Since we
86 * must update directory entries without the benefit of having the vnode
87 * for the directory we must use the vnode for the filesystem. This means
88 * that when a directory is actually read/written (via read, write, or
89 * readdir, or seek) we must use the vnode for the filesystem instead of
90 * the vnode for the directory as would happen in ufs. This is to insure we
91 * retrieve the correct block from the buffer cache since the hash value is
92 * based upon the vnode address and the desired block number.
95 static int msdosfs_wfile(const char *, struct denode
*, fsnode
*);
98 msdosfs_times(struct msdosfsmount
*pmp
, struct denode
*dep
,
99 const struct stat
*st
)
101 #ifndef HAVE_NBTOOL_CONFIG_H
102 struct timespec at
= st
->st_atimespec
;
103 struct timespec mt
= st
->st_mtimespec
;
105 struct timespec at
= { st
->st_atime
, 0 };
106 struct timespec mt
= { st
->st_mtime
, 0 };
108 unix2dostime(&at
, pmp
->pm_gmtoff
, &dep
->de_ADate
, NULL
, NULL
);
109 unix2dostime(&mt
, pmp
->pm_gmtoff
, &dep
->de_MDate
, &dep
->de_MTime
, NULL
);
113 * When we search a directory the blocks containing directory entries are
114 * read and examined. The directory entries contain information that would
115 * normally be in the inode of a unix filesystem. This means that some of
116 * a directory's contents may also be in memory resident denodes (sort of
117 * an inode). This can cause problems if we are searching while some other
118 * process is modifying a directory. To prevent one process from accessing
119 * incompletely modified directory information we depend upon being the
120 * sole owner of a directory block. bread/brelse provide this service.
121 * This being the case, when a process modifies a directory it must first
122 * acquire the disk block that contains the directory entry to be modified.
123 * Then update the disk block and the denode, and then write the disk block
124 * out to disk. This way disk blocks containing directory entries and in
125 * memory denode's will be in synch.
128 msdosfs_findslot(struct denode
*dp
, struct componentname
*cnp
)
139 struct msdosfsmount
*pmp
;
141 struct direntry
*dep
;
142 u_char dosfilename
[12];
144 int chksum
= -1, chksum_ok
;
149 switch (unix2dosfn((const u_char
*)cnp
->cn_nameptr
, dosfilename
,
150 cnp
->cn_namelen
, 0)) {
156 wincnt
= winSlotCnt((const u_char
*)cnp
->cn_nameptr
,
157 cnp
->cn_namelen
) + 1;
161 wincnt
= winSlotCnt((const u_char
*)cnp
->cn_nameptr
,
162 cnp
->cn_namelen
) + 1;
166 if (pmp
->pm_flags
& MSDOSFSMNT_SHORTNAME
)
170 * Suppress search for slots unless creating
171 * file and at end of pathname, in which case
172 * we watch for a place to put the new file in
173 * case it doesn't already exist.
176 DPRINTF(("%s(): dos filename: %s\n", __func__
, dosfilename
));
178 * Search the directory pointed at by vdp for the name pointed at
179 * by cnp->cn_nameptr.
182 * The outer loop ranges over the clusters that make up the
183 * directory. Note that the root directory is different from all
184 * other directories. It has a fixed number of blocks that are not
185 * part of the pool of allocatable clusters. So, we treat it a
186 * little differently. The root directory starts at "cluster" 0.
189 for (frcn
= 0; diroff
< dp
->de_FileSize
; frcn
++) {
190 if ((error
= pcbmap(dp
, frcn
, &bn
, &cluster
, &blsize
)) != 0) {
195 error
= bread(pmp
->pm_devvp
, de_bn2kb(pmp
, bn
), blsize
,
200 for (blkoff
= 0; blkoff
< blsize
;
201 blkoff
+= sizeof(struct direntry
),
202 diroff
+= sizeof(struct direntry
)) {
203 dep
= (struct direntry
*)((char *)bp
->b_data
+ blkoff
);
205 * If the slot is empty and we are still looking
206 * for an empty then remember this one. If the
207 * slot is not empty then check to see if it
208 * matches what we are looking for. If the slot
209 * has never been filled with anything, then the
210 * remainder of the directory has never been used,
211 * so there is no point in searching it.
213 if (dep
->deName
[0] == SLOT_EMPTY
||
214 dep
->deName
[0] == SLOT_DELETED
) {
216 * Drop memory of previous long matches
220 if (slotcount
< wincnt
) {
224 if (dep
->deName
[0] == SLOT_EMPTY
) {
230 * If there wasn't enough space for our
231 * winentries, forget about the empty space
233 if (slotcount
< wincnt
)
237 * Check for Win95 long filename entry
239 if (dep
->deAttributes
== ATTR_WIN95
) {
240 if (pmp
->pm_flags
& MSDOSFSMNT_SHORTNAME
)
243 chksum
= winChkName((const u_char
*)cnp
->cn_nameptr
,
245 (struct winentry
*)dep
,
251 * Ignore volume labels (anywhere, not just
252 * the root directory).
254 if (dep
->deAttributes
& ATTR_VOLUME
) {
260 * Check for a checksum or name match
262 chksum_ok
= (chksum
== winChksum(dep
->deName
));
264 && (!olddos
|| memcmp(dosfilename
, dep
->deName
, 11))) {
268 DPRINTF(("%s(): match blkoff %d, diroff %d\n",
269 __func__
, blkoff
, diroff
));
271 * Remember where this directory
272 * entry came from for whoever did
275 dp
->de_fndoffset
= diroff
;
280 } /* for (blkoff = 0; .... */
282 * Release the buffer holding the directory cluster just
286 } /* for (frcn = 0; ; frcn++) */
290 * We hold no disk buffers at this point.
294 * If we get here we didn't find the entry we were looking for. But
295 * that's ok if we are creating or renaming and are at the end of
296 * the pathname and the directory hasn't been removed.
298 DPRINTF(("%s(): refcnt %ld, slotcount %d, slotoffset %d\n",
299 __func__
, dp
->de_refcnt
, slotcount
, slotoffset
));
301 * Fixup the slot description to point to the place where
302 * we might put the new DOS direntry (putting the Win95
303 * long name entries before that)
309 if (wincnt
> slotcount
) {
310 slotoffset
+= sizeof(struct direntry
) * (wincnt
- slotcount
);
314 * Return an indication of where the new directory
315 * entry should be put.
317 dp
->de_fndoffset
= slotoffset
;
318 dp
->de_fndcnt
= wincnt
- 1;
321 * We return with the directory locked, so that
322 * the parameters we set up above will still be
323 * valid if we actually decide to do a direnter().
324 * We return ni_vp == NULL to indicate that the entry
325 * does not currently exist; we leave a pointer to
326 * the (locked) directory inode in ndp->ni_dvp.
328 * NB - if the directory is unlocked, then this
329 * information cannot be used.
335 * Create a regular file. On entry the directory to contain the file being
336 * created is locked. We must release before we return.
339 msdosfs_mkfile(const char *path
, struct denode
*pdep
, fsnode
*node
)
341 struct componentname cn
;
342 struct denode ndirent
;
345 struct stat
*st
= &node
->inode
->st
;
346 struct msdosfsmount
*pmp
= pdep
->de_pmp
;
348 cn
.cn_nameptr
= node
->name
;
349 cn
.cn_namelen
= strlen(node
->name
);
351 DPRINTF(("%s(name %s, mode 0%o size %zu)\n", __func__
, node
->name
,
352 st
->st_mode
, (size_t)st
->st_size
));
355 * If this is the root directory and there is no space left we
356 * can't do anything. This is because the root directory can not
359 if (pdep
->de_StartCluster
== MSDOSFSROOT
360 && pdep
->de_fndoffset
>= pdep
->de_FileSize
) {
366 * Create a directory entry for the file, then call createde() to
367 * have it installed. NOTE: DOS files are always executable. We
368 * use the absence of the owner write bit to make the file
371 memset(&ndirent
, 0, sizeof(ndirent
));
372 if ((error
= uniqdosname(pdep
, &cn
, ndirent
.de_Name
)) != 0)
375 ndirent
.de_Attributes
= (st
->st_mode
& S_IWUSR
) ?
376 ATTR_ARCHIVE
: ATTR_ARCHIVE
| ATTR_READONLY
;
377 ndirent
.de_StartCluster
= 0;
378 ndirent
.de_FileSize
= 0;
379 ndirent
.de_dev
= pdep
->de_dev
;
380 ndirent
.de_devvp
= pdep
->de_devvp
;
381 ndirent
.de_pmp
= pdep
->de_pmp
;
382 ndirent
.de_flag
= DE_ACCESS
| DE_CREATE
| DE_UPDATE
;
383 msdosfs_times(pmp
, &ndirent
, st
);
384 if ((error
= msdosfs_findslot(pdep
, &cn
)) != 0)
386 if ((error
= createde(&ndirent
, pdep
, &dep
, &cn
)) != 0)
388 if ((error
= msdosfs_wfile(path
, dep
, node
)) != 0)
397 msdosfs_updatede(struct denode
*dep
)
400 struct direntry
*dirp
;
403 dep
->de_flag
&= ~DE_MODIFIED
;
404 error
= readde(dep
, &bp
, &dirp
);
407 DE_EXTERNALIZE(dirp
, dep
);
413 * Write data to a file or directory.
416 msdosfs_wfile(const char *path
, struct denode
*dep
, fsnode
*node
)
419 size_t osize
= dep
->de_FileSize
;
420 struct stat
*st
= &node
->inode
->st
;
422 struct msdosfsmount
*pmp
= dep
->de_pmp
;
427 error
= 0; /* XXX: gcc/vax */
428 DPRINTF(("%s(diroff %lu, dirclust %lu, startcluster %lu)\n", __func__
,
429 dep
->de_diroffset
, dep
->de_dirclust
, dep
->de_StartCluster
));
430 if (st
->st_size
== 0)
433 /* Don't bother to try to write files larger than the fs limit */
434 if (st
->st_size
> MSDOSFS_FILESIZE_MAX
) {
440 DPRINTF(("%s(nsize=%zu, osize=%zu)\n", __func__
, nsize
, osize
));
442 if ((error
= deextend(dep
, nsize
, NULL
)) != 0) {
446 if ((error
= msdosfs_updatede(dep
)) != 0) {
452 if ((fd
= open(path
, O_RDONLY
)) == -1)
453 err(1, "open %s", path
);
455 if ((dat
= mmap(0, nsize
, PROT_READ
, MAP_FILE
| MAP_PRIVATE
, fd
, 0))
457 DPRINTF(("%s: mmap %s %s", __func__
, node
->name
,
464 for (offs
= 0; offs
< nsize
;) {
467 u_long on
= offs
& pmp
->pm_crbomask
;
469 cn
= dep
->de_StartCluster
;
470 if (cn
== MSDOSFSROOT
) {
471 DPRINTF(("%s: bad lbn %lu", __func__
, cn
));
474 bn
= cntobn(pmp
, cn
);
475 blsize
= pmp
->pm_bpcluster
;
477 if ((error
= pcbmap(dep
, cn
++, &bn
, NULL
, &blsize
)) != 0) {
478 DPRINTF(("%s: pcbmap %lu", __func__
, bn
));
482 DPRINTF(("%s(cn=%lu, bn=%llu/%llu, blsize=%d)\n", __func__
,
483 cn
, (unsigned long long)bn
,
484 (unsigned long long)de_bn2kb(pmp
, bn
), blsize
));
485 if ((error
= bread(pmp
->pm_devvp
, de_bn2kb(pmp
, bn
), blsize
,
487 DPRINTF(("bread %d\n", error
));
490 cpsize
= MIN((nsize
- offs
), blsize
- on
);
491 memcpy((char *)bp
->b_data
+ on
, dat
+ offs
, cpsize
);
504 static const struct {
506 struct direntry dotdot
;
508 { ". ", " ", /* the . entry */
509 ATTR_DIRECTORY
, /* file attribute */
511 0, { 0, 0 }, { 0, 0 }, /* create time & date */
512 { 0, 0 }, /* access date */
513 { 0, 0 }, /* high bits of start cluster */
514 { 210, 4 }, { 210, 4 }, /* modify time & date */
515 { 0, 0 }, /* startcluster */
516 { 0, 0, 0, 0 } /* filesize */
518 { ".. ", " ", /* the .. entry */
519 ATTR_DIRECTORY
, /* file attribute */
521 0, { 0, 0 }, { 0, 0 }, /* create time & date */
522 { 0, 0 }, /* access date */
523 { 0, 0 }, /* high bits of start cluster */
524 { 210, 4 }, { 210, 4 }, /* modify time & date */
525 { 0, 0 }, /* startcluster */
526 { 0, 0, 0, 0 } /* filesize */
531 msdosfs_mkdire(const char *path
, struct denode
*pdep
, fsnode
*node
) {
532 struct denode ndirent
;
534 struct componentname cn
;
535 struct stat
*st
= &node
->inode
->st
;
536 struct msdosfsmount
*pmp
= pdep
->de_pmp
;
538 u_long newcluster
, pcl
, bn
;
540 struct direntry
*denp
;
543 cn
.cn_nameptr
= node
->name
;
544 cn
.cn_namelen
= strlen(node
->name
);
546 * If this is the root directory and there is no space left we
547 * can't do anything. This is because the root directory can not
550 if (pdep
->de_StartCluster
== MSDOSFSROOT
551 && pdep
->de_fndoffset
>= pdep
->de_FileSize
) {
557 * Allocate a cluster to hold the about to be created directory.
559 error
= clusteralloc(pmp
, 0, 1, &newcluster
, NULL
);
563 memset(&ndirent
, 0, sizeof(ndirent
));
564 ndirent
.de_pmp
= pmp
;
565 ndirent
.de_flag
= DE_ACCESS
| DE_CREATE
| DE_UPDATE
;
566 msdosfs_times(pmp
, &ndirent
, st
);
569 * Now fill the cluster with the "." and ".." entries. And write
570 * the cluster to disk. This way it is there for the parent
571 * directory to be pointing at if there were a crash.
573 bn
= cntobn(pmp
, newcluster
);
574 lbn
= de_bn2kb(pmp
, bn
);
575 DPRINTF(("%s(newcluster %lu, bn=%lu, lbn=%lu)\n", __func__
, newcluster
,
577 /* always succeeds */
578 bp
= getblk(pmp
->pm_devvp
, lbn
, pmp
->pm_bpcluster
, 0, 0);
579 memset(bp
->b_data
, 0, pmp
->pm_bpcluster
);
580 memcpy(bp
->b_data
, &dosdirtemplate
, sizeof dosdirtemplate
);
581 denp
= (struct direntry
*)bp
->b_data
;
582 putushort(denp
[0].deStartCluster
, newcluster
);
583 putushort(denp
[0].deCDate
, ndirent
.de_CDate
);
584 putushort(denp
[0].deCTime
, ndirent
.de_CTime
);
585 denp
[0].deCHundredth
= ndirent
.de_CHun
;
586 putushort(denp
[0].deADate
, ndirent
.de_ADate
);
587 putushort(denp
[0].deMDate
, ndirent
.de_MDate
);
588 putushort(denp
[0].deMTime
, ndirent
.de_MTime
);
589 pcl
= pdep
->de_StartCluster
;
590 DPRINTF(("%s(pcl %lu, rootdirblk=%lu)\n", __func__
, pcl
,
591 pmp
->pm_rootdirblk
));
592 if (FAT32(pmp
) && pcl
== pmp
->pm_rootdirblk
)
594 putushort(denp
[1].deStartCluster
, pcl
);
595 putushort(denp
[1].deCDate
, ndirent
.de_CDate
);
596 putushort(denp
[1].deCTime
, ndirent
.de_CTime
);
597 denp
[1].deCHundredth
= ndirent
.de_CHun
;
598 putushort(denp
[1].deADate
, ndirent
.de_ADate
);
599 putushort(denp
[1].deMDate
, ndirent
.de_MDate
);
600 putushort(denp
[1].deMTime
, ndirent
.de_MTime
);
602 putushort(denp
[0].deHighClust
, newcluster
>> 16);
603 putushort(denp
[1].deHighClust
, pdep
->de_StartCluster
>> 16);
605 putushort(denp
[0].deHighClust
, 0);
606 putushort(denp
[1].deHighClust
, 0);
609 if ((error
= bwrite(bp
)) != 0)
613 * Now build up a directory entry pointing to the newly allocated
614 * cluster. This will be written to an empty slot in the parent
617 if ((error
= uniqdosname(pdep
, &cn
, ndirent
.de_Name
)) != 0)
620 ndirent
.de_Attributes
= ATTR_DIRECTORY
;
621 ndirent
.de_StartCluster
= newcluster
;
622 ndirent
.de_FileSize
= 0;
623 ndirent
.de_dev
= pdep
->de_dev
;
624 ndirent
.de_devvp
= pdep
->de_devvp
;
625 ndirent
.de_pmp
= pdep
->de_pmp
;
626 if ((error
= msdosfs_findslot(pdep
, &cn
)) != 0)
628 if ((error
= createde(&ndirent
, pdep
, &dep
, &cn
)) != 0)
630 if ((error
= msdosfs_updatede(dep
)) != 0)
635 clusterfree(pmp
, newcluster
, NULL
);