1 /* $NetBSD: msdosfs_denode.c,v 1.7 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.
50 #if HAVE_NBTOOL_CONFIG_H
51 #include "nbtool_config.h"
54 #include <sys/cdefs.h>
55 __KERNEL_RCSID(0, "$NetBSD: msdosfs_denode.c,v 1.7 2015/03/29 05:52:59 agc Exp $");
57 #include <sys/param.h>
61 #include <fs/msdosfs/bpb.h>
62 #include <fs/msdosfs/msdosfsmount.h>
63 #include <fs/msdosfs/direntry.h>
64 #include <fs/msdosfs/denode.h>
65 #include <fs/msdosfs/fat.h>
70 * If deget() succeeds it returns with the gotten denode locked().
72 * pmp - address of msdosfsmount structure of the filesystem containing
73 * the denode of interest. The pm_dev field and the address of
74 * the msdosfsmount structure are used.
75 * dirclust - which cluster bp contains, if dirclust is 0 (root directory)
76 * diroffset is relative to the beginning of the root directory,
77 * otherwise it is cluster relative.
78 * diroffset - offset past begin of cluster of denode we want
79 * depp - returns the address of the gotten denode.
82 deget(struct msdosfsmount
*pmp
, u_long dirclust
, u_long diroffset
,
84 /* pmp: so we know the maj/min number */
85 /* dirclust: cluster this dir entry came from */
86 /* diroffset: index of entry within the cluster */
87 /* depp: returns the addr of the gotten denode */
90 struct direntry
*direntptr
;
95 printf("deget(pmp %p, dirclust %lu, diroffset %lx, depp %p)\n",
96 pmp
, dirclust
, diroffset
, depp
);
100 * On FAT32 filesystems, root is a (more or less) normal
103 if (FAT32(pmp
) && dirclust
== MSDOSFSROOT
)
104 dirclust
= pmp
->pm_rootdirblk
;
106 ldep
= ecalloc(1, sizeof(*ldep
));
107 ldep
->de_vnode
= NULL
;
111 ldep
->de_dev
= pmp
->pm_dev
;
112 ldep
->de_dirclust
= dirclust
;
113 ldep
->de_diroffset
= diroffset
;
115 ldep
->de_devvp
= pmp
->pm_devvp
;
119 * Copy the directory entry into the denode area of the vnode.
121 if ((dirclust
== MSDOSFSROOT
122 || (FAT32(pmp
) && dirclust
== pmp
->pm_rootdirblk
))
123 && diroffset
== MSDOSFSROOT_OFS
) {
125 * Directory entry for the root directory. There isn't one,
126 * so we manufacture one. We should probably rummage
127 * through the root directory and find a label entry (if it
128 * exists), and then use the time and date from that entry
129 * as the time and date for the root denode.
131 ldep
->de_vnode
= (struct vnode
*)-1;
133 ldep
->de_Attributes
= ATTR_DIRECTORY
;
135 ldep
->de_StartCluster
= pmp
->pm_rootdirblk
;
136 /* de_FileSize will be filled in further down */
138 ldep
->de_StartCluster
= MSDOSFSROOT
;
139 ldep
->de_FileSize
= pmp
->pm_rootdirsize
* pmp
->pm_BytesPerSec
;
142 * fill in time and date so that dos2unixtime() doesn't
143 * spit up when called from msdosfs_getattr() with root
147 ldep
->de_CTime
= 0x0000; /* 00:00:00 */
148 ldep
->de_CDate
= (0 << DD_YEAR_SHIFT
) | (1 << DD_MONTH_SHIFT
)
149 | (1 << DD_DAY_SHIFT
);
151 ldep
->de_ADate
= ldep
->de_CDate
;
152 ldep
->de_MTime
= ldep
->de_CTime
;
153 ldep
->de_MDate
= ldep
->de_CDate
;
154 /* leave the other fields as garbage */
156 error
= readep(pmp
, dirclust
, diroffset
, &bp
, &direntptr
);
158 ldep
->de_devvp
= NULL
;
159 ldep
->de_Name
[0] = SLOT_DELETED
;
162 DE_INTERNALIZE(ldep
, direntptr
);
167 * Fill in a few fields of the vnode and finish filling in the
168 * denode. Then return the address of the found denode.
170 if (ldep
->de_Attributes
& ATTR_DIRECTORY
) {
172 * Since DOS directory entries that describe directories
173 * have 0 in the filesize field, we take this opportunity
174 * to find out the length of the directory and plug it into
175 * the denode structure.
179 if (ldep
->de_StartCluster
!= MSDOSFSROOT
) {
180 error
= pcbmap(ldep
, CLUST_END
, 0, &size
, 0);
181 if (error
== E2BIG
) {
182 ldep
->de_FileSize
= de_cn2off(pmp
, size
);
185 printf("deget(): pcbmap returned %d\n", error
);
193 * Truncate the file described by dep to the length specified by length.
196 detrunc(struct denode
*dep
, u_long length
, int flags
, struct kauth_cred
*cred
)
201 u_long chaintofree
= 0;
202 daddr_t bn
, lastblock
;
204 int isadir
= dep
->de_Attributes
& ATTR_DIRECTORY
;
206 struct msdosfsmount
*pmp
= dep
->de_pmp
;
209 printf("detrunc(): file %s, length %lu, flags %x\n", dep
->de_Name
, length
, flags
);
213 * Disallow attempts to truncate the root directory since it is of
214 * fixed size. That's just the way dos filesystems are. We use
215 * the VROOT bit in the vnode because checking for the directory
216 * bit and a startcluster of 0 in the denode is not adequate to
217 * recognize the root directory at this point in a file or
220 if (dep
->de_vnode
!= NULL
&& !FAT32(pmp
)) {
221 printf("detrunc(): can't truncate root directory, clust %ld, offset %ld\n",
222 dep
->de_dirclust
, dep
->de_diroffset
);
226 if (dep
->de_FileSize
< length
)
227 return (deextend(dep
, length
, cred
));
228 lastblock
= de_clcount(pmp
, length
) - 1;
231 * If the desired length is 0 then remember the starting cluster of
232 * the file and set the StartCluster field in the directory entry
233 * to 0. If the desired length is not zero, then get the number of
234 * the last cluster in the shortened file. Then get the number of
235 * the first cluster in the part of the file that is to be freed.
236 * Then set the next cluster pointer in the last cluster of the
237 * file to CLUST_EOFE.
240 chaintofree
= dep
->de_StartCluster
;
241 dep
->de_StartCluster
= 0;
244 error
= pcbmap(dep
, lastblock
, 0, &eofentry
, 0);
247 printf("detrunc(): pcbmap fails %d\n", error
);
254 * If the new length is not a multiple of the cluster size then we
255 * must zero the tail end of the new last cluster in case it
256 * becomes part of the file again because of a seek.
258 if ((boff
= length
& pmp
->pm_crbomask
) != 0) {
260 bn
= cntobn(pmp
, eofentry
);
261 error
= bread(pmp
->pm_devvp
, de_bn2kb(pmp
, bn
),
262 pmp
->pm_bpcluster
, B_MODIFY
, &bp
);
265 printf("detrunc(): bread fails %d\n", error
);
269 memset((char *)bp
->b_data
+ boff
, 0,
270 pmp
->pm_bpcluster
- boff
);
279 * Write out the updated directory entry. Even if the update fails
280 * we free the trailing clusters.
282 dep
->de_FileSize
= length
;
284 dep
->de_flag
|= DE_UPDATE
|DE_MODIFIED
;
286 printf("detrunc(): allerror %d, eofentry %lu\n",
291 * If we need to break the cluster chain for the file then do it
294 if (eofentry
!= (u_long
)~0) {
295 error
= fatentry(FAT_GET_AND_SET
, pmp
, eofentry
,
296 &chaintofree
, CLUST_EOFE
);
299 printf("detrunc(): fatentry errors %d\n", error
);
306 * Now free the clusters removed from the file because of the
309 if (chaintofree
!= 0 && !MSDOSFSEOF(chaintofree
, pmp
->pm_fatmask
))
310 freeclusterchain(pmp
, chaintofree
);
316 * Extend the file described by dep to length specified by length.
319 deextend(struct denode
*dep
, u_long length
, struct kauth_cred
*cred
)
321 struct msdosfsmount
*pmp
= dep
->de_pmp
;
326 * The root of a DOS filesystem cannot be extended.
328 if (dep
->de_vnode
!= NULL
&& !FAT32(pmp
))
332 * Directories cannot be extended.
334 if (dep
->de_Attributes
& ATTR_DIRECTORY
)
337 if (length
<= dep
->de_FileSize
)
341 * Compute the number of clusters to allocate.
343 count
= de_clcount(pmp
, length
) - de_clcount(pmp
, dep
->de_FileSize
);
345 if (count
> pmp
->pm_freeclustercount
)
347 error
= extendfile(dep
, count
, NULL
, NULL
, DE_CLEAR
);
349 /* truncate the added clusters away again */
350 (void) detrunc(dep
, dep
->de_FileSize
, 0, cred
);
356 * Zero extend file range; ubc_zerorange() uses ubc_alloc() and a
357 * memset(); we set the write size so ubc won't read in file data that
360 dep
->de_FileSize
= length
;
361 dep
->de_flag
|= DE_UPDATE
|DE_MODIFIED
;