msdosfs_denode.c revision 1.5.4.3 1 1.5.4.2 tls /* $NetBSD: msdosfs_denode.c,v 1.5.4.3 2014/08/20 00:05:09 tls Exp $ */
2 1.5.4.2 tls
3 1.5.4.2 tls /*-
4 1.5.4.2 tls * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
5 1.5.4.2 tls * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
6 1.5.4.2 tls * All rights reserved.
7 1.5.4.2 tls * Original code by Paul Popelka (paulp (at) uts.amdahl.com) (see below).
8 1.5.4.2 tls *
9 1.5.4.2 tls * Redistribution and use in source and binary forms, with or without
10 1.5.4.2 tls * modification, are permitted provided that the following conditions
11 1.5.4.2 tls * are met:
12 1.5.4.2 tls * 1. Redistributions of source code must retain the above copyright
13 1.5.4.2 tls * notice, this list of conditions and the following disclaimer.
14 1.5.4.2 tls * 2. Redistributions in binary form must reproduce the above copyright
15 1.5.4.2 tls * notice, this list of conditions and the following disclaimer in the
16 1.5.4.2 tls * documentation and/or other materials provided with the distribution.
17 1.5.4.2 tls * 3. All advertising materials mentioning features or use of this software
18 1.5.4.2 tls * must display the following acknowledgement:
19 1.5.4.2 tls * This product includes software developed by TooLs GmbH.
20 1.5.4.2 tls * 4. The name of TooLs GmbH may not be used to endorse or promote products
21 1.5.4.2 tls * derived from this software without specific prior written permission.
22 1.5.4.2 tls *
23 1.5.4.2 tls * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
24 1.5.4.2 tls * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 1.5.4.2 tls * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 1.5.4.2 tls * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27 1.5.4.2 tls * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28 1.5.4.2 tls * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
29 1.5.4.2 tls * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30 1.5.4.2 tls * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
31 1.5.4.2 tls * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32 1.5.4.2 tls * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 1.5.4.2 tls */
34 1.5.4.2 tls /*
35 1.5.4.2 tls * Written by Paul Popelka (paulp (at) uts.amdahl.com)
36 1.5.4.2 tls *
37 1.5.4.2 tls * You can do anything you want with this software, just don't say you wrote
38 1.5.4.2 tls * it, and don't remove this notice.
39 1.5.4.2 tls *
40 1.5.4.2 tls * This software is provided "as is".
41 1.5.4.2 tls *
42 1.5.4.2 tls * The author supplies this software to be publicly redistributed on the
43 1.5.4.2 tls * understanding that the author is not responsible for the correct
44 1.5.4.2 tls * functioning of this software in any circumstances and is not liable for
45 1.5.4.2 tls * any damages caused by this software.
46 1.5.4.2 tls *
47 1.5.4.2 tls * October 1992
48 1.5.4.2 tls */
49 1.5.4.2 tls
50 1.5.4.2 tls #if HAVE_NBTOOL_CONFIG_H
51 1.5.4.2 tls #include "nbtool_config.h"
52 1.5.4.2 tls #endif
53 1.5.4.2 tls
54 1.5.4.2 tls #include <sys/cdefs.h>
55 1.5.4.2 tls __KERNEL_RCSID(0, "$NetBSD: msdosfs_denode.c,v 1.5.4.3 2014/08/20 00:05:09 tls Exp $");
56 1.5.4.2 tls
57 1.5.4.2 tls #include <sys/param.h>
58 1.5.4.2 tls
59 1.5.4.2 tls #include <ffs/buf.h>
60 1.5.4.2 tls
61 1.5.4.2 tls #include <fs/msdosfs/bpb.h>
62 1.5.4.2 tls #include <fs/msdosfs/msdosfsmount.h>
63 1.5.4.2 tls #include <fs/msdosfs/direntry.h>
64 1.5.4.2 tls #include <fs/msdosfs/denode.h>
65 1.5.4.2 tls #include <fs/msdosfs/fat.h>
66 1.5.4.2 tls
67 1.5.4.2 tls #include <util.h>
68 1.5.4.2 tls
69 1.5.4.2 tls /*
70 1.5.4.2 tls * If deget() succeeds it returns with the gotten denode locked().
71 1.5.4.2 tls *
72 1.5.4.2 tls * pmp - address of msdosfsmount structure of the filesystem containing
73 1.5.4.2 tls * the denode of interest. The pm_dev field and the address of
74 1.5.4.2 tls * the msdosfsmount structure are used.
75 1.5.4.2 tls * dirclust - which cluster bp contains, if dirclust is 0 (root directory)
76 1.5.4.2 tls * diroffset is relative to the beginning of the root directory,
77 1.5.4.2 tls * otherwise it is cluster relative.
78 1.5.4.2 tls * diroffset - offset past begin of cluster of denode we want
79 1.5.4.2 tls * depp - returns the address of the gotten denode.
80 1.5.4.2 tls */
81 1.5.4.2 tls int
82 1.5.4.2 tls deget(struct msdosfsmount *pmp, u_long dirclust, u_long diroffset,
83 1.5.4.2 tls struct denode **depp)
84 1.5.4.2 tls /* pmp: so we know the maj/min number */
85 1.5.4.2 tls /* dirclust: cluster this dir entry came from */
86 1.5.4.2 tls /* diroffset: index of entry within the cluster */
87 1.5.4.2 tls /* depp: returns the addr of the gotten denode */
88 1.5.4.2 tls {
89 1.5.4.2 tls int error;
90 1.5.4.2 tls struct direntry *direntptr;
91 1.5.4.2 tls struct denode *ldep;
92 1.5.4.2 tls struct buf *bp;
93 1.5.4.2 tls
94 1.5.4.2 tls #ifdef MSDOSFS_DEBUG
95 1.5.4.2 tls printf("deget(pmp %p, dirclust %lu, diroffset %lx, depp %p)\n",
96 1.5.4.2 tls pmp, dirclust, diroffset, depp);
97 1.5.4.2 tls #endif
98 1.5.4.2 tls
99 1.5.4.2 tls /*
100 1.5.4.2 tls * On FAT32 filesystems, root is a (more or less) normal
101 1.5.4.2 tls * directory
102 1.5.4.2 tls */
103 1.5.4.2 tls if (FAT32(pmp) && dirclust == MSDOSFSROOT)
104 1.5.4.2 tls dirclust = pmp->pm_rootdirblk;
105 1.5.4.2 tls
106 1.5.4.2 tls ldep = ecalloc(1, sizeof(*ldep));
107 1.5.4.2 tls ldep->de_vnode = NULL;
108 1.5.4.2 tls ldep->de_flag = 0;
109 1.5.4.2 tls ldep->de_devvp = 0;
110 1.5.4.2 tls ldep->de_lockf = 0;
111 1.5.4.2 tls ldep->de_dev = pmp->pm_dev;
112 1.5.4.2 tls ldep->de_dirclust = dirclust;
113 1.5.4.2 tls ldep->de_diroffset = diroffset;
114 1.5.4.2 tls ldep->de_pmp = pmp;
115 1.5.4.2 tls ldep->de_devvp = pmp->pm_devvp;
116 1.5.4.2 tls ldep->de_refcnt = 1;
117 1.5.4.2 tls fc_purge(ldep, 0);
118 1.5.4.2 tls /*
119 1.5.4.2 tls * Copy the directory entry into the denode area of the vnode.
120 1.5.4.2 tls */
121 1.5.4.2 tls if ((dirclust == MSDOSFSROOT
122 1.5.4.2 tls || (FAT32(pmp) && dirclust == pmp->pm_rootdirblk))
123 1.5.4.2 tls && diroffset == MSDOSFSROOT_OFS) {
124 1.5.4.2 tls /*
125 1.5.4.2 tls * Directory entry for the root directory. There isn't one,
126 1.5.4.2 tls * so we manufacture one. We should probably rummage
127 1.5.4.2 tls * through the root directory and find a label entry (if it
128 1.5.4.2 tls * exists), and then use the time and date from that entry
129 1.5.4.2 tls * as the time and date for the root denode.
130 1.5.4.2 tls */
131 1.5.4.2 tls ldep->de_vnode = (struct vnode *)-1;
132 1.5.4.2 tls
133 1.5.4.2 tls ldep->de_Attributes = ATTR_DIRECTORY;
134 1.5.4.2 tls if (FAT32(pmp))
135 1.5.4.2 tls ldep->de_StartCluster = pmp->pm_rootdirblk;
136 1.5.4.2 tls /* de_FileSize will be filled in further down */
137 1.5.4.2 tls else {
138 1.5.4.2 tls ldep->de_StartCluster = MSDOSFSROOT;
139 1.5.4.2 tls ldep->de_FileSize = pmp->pm_rootdirsize * pmp->pm_BytesPerSec;
140 1.5.4.2 tls }
141 1.5.4.2 tls /*
142 1.5.4.2 tls * fill in time and date so that dos2unixtime() doesn't
143 1.5.4.2 tls * spit up when called from msdosfs_getattr() with root
144 1.5.4.2 tls * denode
145 1.5.4.2 tls */
146 1.5.4.2 tls ldep->de_CHun = 0;
147 1.5.4.2 tls ldep->de_CTime = 0x0000; /* 00:00:00 */
148 1.5.4.2 tls ldep->de_CDate = (0 << DD_YEAR_SHIFT) | (1 << DD_MONTH_SHIFT)
149 1.5.4.2 tls | (1 << DD_DAY_SHIFT);
150 1.5.4.2 tls /* Jan 1, 1980 */
151 1.5.4.2 tls ldep->de_ADate = ldep->de_CDate;
152 1.5.4.2 tls ldep->de_MTime = ldep->de_CTime;
153 1.5.4.2 tls ldep->de_MDate = ldep->de_CDate;
154 1.5.4.2 tls /* leave the other fields as garbage */
155 1.5.4.2 tls } else {
156 1.5.4.2 tls error = readep(pmp, dirclust, diroffset, &bp, &direntptr);
157 1.5.4.2 tls if (error) {
158 1.5.4.2 tls ldep->de_devvp = NULL;
159 1.5.4.2 tls ldep->de_Name[0] = SLOT_DELETED;
160 1.5.4.2 tls return (error);
161 1.5.4.2 tls }
162 1.5.4.2 tls DE_INTERNALIZE(ldep, direntptr);
163 1.5.4.2 tls brelse(bp, 0);
164 1.5.4.2 tls }
165 1.5.4.2 tls
166 1.5.4.2 tls /*
167 1.5.4.2 tls * Fill in a few fields of the vnode and finish filling in the
168 1.5.4.2 tls * denode. Then return the address of the found denode.
169 1.5.4.2 tls */
170 1.5.4.2 tls if (ldep->de_Attributes & ATTR_DIRECTORY) {
171 1.5.4.2 tls /*
172 1.5.4.2 tls * Since DOS directory entries that describe directories
173 1.5.4.2 tls * have 0 in the filesize field, we take this opportunity
174 1.5.4.2 tls * to find out the length of the directory and plug it into
175 1.5.4.2 tls * the denode structure.
176 1.5.4.2 tls */
177 1.5.4.2 tls u_long size;
178 1.5.4.2 tls
179 1.5.4.2 tls if (ldep->de_StartCluster != MSDOSFSROOT) {
180 1.5.4.2 tls error = pcbmap(ldep, CLUST_END, 0, &size, 0);
181 1.5.4.2 tls if (error == E2BIG) {
182 1.5.4.2 tls ldep->de_FileSize = de_cn2off(pmp, size);
183 1.5.4.2 tls error = 0;
184 1.5.4.2 tls } else
185 1.5.4.2 tls printf("deget(): pcbmap returned %d\n", error);
186 1.5.4.2 tls }
187 1.5.4.2 tls }
188 1.5.4.2 tls *depp = ldep;
189 1.5.4.2 tls return (0);
190 1.5.4.2 tls }
191 1.5.4.2 tls
192 1.5.4.2 tls /*
193 1.5.4.2 tls * Truncate the file described by dep to the length specified by length.
194 1.5.4.2 tls */
195 1.5.4.2 tls int
196 1.5.4.2 tls detrunc(struct denode *dep, u_long length, int flags, struct kauth_cred *cred)
197 1.5.4.2 tls {
198 1.5.4.2 tls int error;
199 1.5.4.2 tls int allerror = 0;
200 1.5.4.2 tls u_long eofentry;
201 1.5.4.2 tls u_long chaintofree = 0;
202 1.5.4.2 tls daddr_t bn, lastblock;
203 1.5.4.2 tls int boff;
204 1.5.4.2 tls int isadir = dep->de_Attributes & ATTR_DIRECTORY;
205 1.5.4.2 tls struct buf *bp;
206 1.5.4.2 tls struct msdosfsmount *pmp = dep->de_pmp;
207 1.5.4.2 tls
208 1.5.4.2 tls #ifdef MSDOSFS_DEBUG
209 1.5.4.2 tls printf("detrunc(): file %s, length %lu, flags %x\n", dep->de_Name, length, flags);
210 1.5.4.2 tls #endif
211 1.5.4.2 tls
212 1.5.4.2 tls /*
213 1.5.4.2 tls * Disallow attempts to truncate the root directory since it is of
214 1.5.4.2 tls * fixed size. That's just the way dos filesystems are. We use
215 1.5.4.2 tls * the VROOT bit in the vnode because checking for the directory
216 1.5.4.2 tls * bit and a startcluster of 0 in the denode is not adequate to
217 1.5.4.2 tls * recognize the root directory at this point in a file or
218 1.5.4.2 tls * directory's life.
219 1.5.4.2 tls */
220 1.5.4.2 tls if (dep->de_vnode != NULL && !FAT32(pmp)) {
221 1.5.4.2 tls printf("detrunc(): can't truncate root directory, clust %ld, offset %ld\n",
222 1.5.4.2 tls dep->de_dirclust, dep->de_diroffset);
223 1.5.4.2 tls return (EINVAL);
224 1.5.4.2 tls }
225 1.5.4.2 tls
226 1.5.4.2 tls if (dep->de_FileSize < length)
227 1.5.4.2 tls return (deextend(dep, length, cred));
228 1.5.4.2 tls lastblock = de_clcount(pmp, length) - 1;
229 1.5.4.2 tls
230 1.5.4.2 tls /*
231 1.5.4.2 tls * If the desired length is 0 then remember the starting cluster of
232 1.5.4.2 tls * the file and set the StartCluster field in the directory entry
233 1.5.4.2 tls * to 0. If the desired length is not zero, then get the number of
234 1.5.4.2 tls * the last cluster in the shortened file. Then get the number of
235 1.5.4.2 tls * the first cluster in the part of the file that is to be freed.
236 1.5.4.2 tls * Then set the next cluster pointer in the last cluster of the
237 1.5.4.2 tls * file to CLUST_EOFE.
238 1.5.4.2 tls */
239 1.5.4.2 tls if (length == 0) {
240 1.5.4.2 tls chaintofree = dep->de_StartCluster;
241 1.5.4.2 tls dep->de_StartCluster = 0;
242 1.5.4.2 tls eofentry = ~0;
243 1.5.4.2 tls } else {
244 1.5.4.2 tls error = pcbmap(dep, lastblock, 0, &eofentry, 0);
245 1.5.4.2 tls if (error) {
246 1.5.4.2 tls #ifdef MSDOSFS_DEBUG
247 1.5.4.2 tls printf("detrunc(): pcbmap fails %d\n", error);
248 1.5.4.2 tls #endif
249 1.5.4.2 tls return (error);
250 1.5.4.2 tls }
251 1.5.4.2 tls }
252 1.5.4.2 tls
253 1.5.4.2 tls /*
254 1.5.4.2 tls * If the new length is not a multiple of the cluster size then we
255 1.5.4.2 tls * must zero the tail end of the new last cluster in case it
256 1.5.4.2 tls * becomes part of the file again because of a seek.
257 1.5.4.2 tls */
258 1.5.4.2 tls if ((boff = length & pmp->pm_crbomask) != 0) {
259 1.5.4.2 tls if (isadir) {
260 1.5.4.2 tls bn = cntobn(pmp, eofentry);
261 1.5.4.2 tls error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn),
262 1.5.4.2 tls pmp->pm_bpcluster, NOCRED, B_MODIFY, &bp);
263 1.5.4.2 tls if (error) {
264 1.5.4.2 tls #ifdef MSDOSFS_DEBUG
265 1.5.4.2 tls printf("detrunc(): bread fails %d\n", error);
266 1.5.4.2 tls #endif
267 1.5.4.2 tls return (error);
268 1.5.4.2 tls }
269 1.5.4.2 tls memset((char *)bp->b_data + boff, 0,
270 1.5.4.2 tls pmp->pm_bpcluster - boff);
271 1.5.4.2 tls if (flags & IO_SYNC)
272 1.5.4.2 tls bwrite(bp);
273 1.5.4.2 tls else
274 1.5.4.2 tls bdwrite(bp);
275 1.5.4.2 tls }
276 1.5.4.2 tls }
277 1.5.4.2 tls
278 1.5.4.2 tls /*
279 1.5.4.2 tls * Write out the updated directory entry. Even if the update fails
280 1.5.4.2 tls * we free the trailing clusters.
281 1.5.4.2 tls */
282 1.5.4.2 tls dep->de_FileSize = length;
283 1.5.4.2 tls if (!isadir)
284 1.5.4.2 tls dep->de_flag |= DE_UPDATE|DE_MODIFIED;
285 1.5.4.2 tls #ifdef MSDOSFS_DEBUG
286 1.5.4.2 tls printf("detrunc(): allerror %d, eofentry %lu\n",
287 1.5.4.2 tls allerror, eofentry);
288 1.5.4.2 tls #endif
289 1.5.4.2 tls
290 1.5.4.2 tls /*
291 1.5.4.2 tls * If we need to break the cluster chain for the file then do it
292 1.5.4.2 tls * now.
293 1.5.4.2 tls */
294 1.5.4.2 tls if (eofentry != (u_long)~0) {
295 1.5.4.2 tls error = fatentry(FAT_GET_AND_SET, pmp, eofentry,
296 1.5.4.2 tls &chaintofree, CLUST_EOFE);
297 1.5.4.2 tls if (error) {
298 1.5.4.2 tls #ifdef MSDOSFS_DEBUG
299 1.5.4.2 tls printf("detrunc(): fatentry errors %d\n", error);
300 1.5.4.2 tls #endif
301 1.5.4.2 tls return (error);
302 1.5.4.2 tls }
303 1.5.4.2 tls }
304 1.5.4.2 tls
305 1.5.4.2 tls /*
306 1.5.4.2 tls * Now free the clusters removed from the file because of the
307 1.5.4.2 tls * truncation.
308 1.5.4.2 tls */
309 1.5.4.2 tls if (chaintofree != 0 && !MSDOSFSEOF(chaintofree, pmp->pm_fatmask))
310 1.5.4.2 tls freeclusterchain(pmp, chaintofree);
311 1.5.4.2 tls
312 1.5.4.2 tls return (allerror);
313 1.5.4.2 tls }
314 1.5.4.2 tls
315 1.5.4.2 tls /*
316 1.5.4.2 tls * Extend the file described by dep to length specified by length.
317 1.5.4.2 tls */
318 1.5.4.2 tls int
319 1.5.4.2 tls deextend(struct denode *dep, u_long length, struct kauth_cred *cred)
320 1.5.4.2 tls {
321 1.5.4.2 tls struct msdosfsmount *pmp = dep->de_pmp;
322 1.5.4.3 tls u_long count;
323 1.5.4.2 tls int error;
324 1.5.4.2 tls
325 1.5.4.2 tls /*
326 1.5.4.2 tls * The root of a DOS filesystem cannot be extended.
327 1.5.4.2 tls */
328 1.5.4.2 tls if (dep->de_vnode != NULL && !FAT32(pmp))
329 1.5.4.2 tls return EINVAL;
330 1.5.4.2 tls
331 1.5.4.2 tls /*
332 1.5.4.2 tls * Directories cannot be extended.
333 1.5.4.2 tls */
334 1.5.4.2 tls if (dep->de_Attributes & ATTR_DIRECTORY)
335 1.5.4.2 tls return EISDIR;
336 1.5.4.2 tls
337 1.5.4.2 tls if (length <= dep->de_FileSize)
338 1.5.4.2 tls return E2BIG;
339 1.5.4.2 tls
340 1.5.4.2 tls /*
341 1.5.4.2 tls * Compute the number of clusters to allocate.
342 1.5.4.2 tls */
343 1.5.4.2 tls count = de_clcount(pmp, length) - de_clcount(pmp, dep->de_FileSize);
344 1.5.4.2 tls if (count > 0) {
345 1.5.4.2 tls if (count > pmp->pm_freeclustercount)
346 1.5.4.2 tls return (ENOSPC);
347 1.5.4.2 tls error = extendfile(dep, count, NULL, NULL, DE_CLEAR);
348 1.5.4.2 tls if (error) {
349 1.5.4.2 tls /* truncate the added clusters away again */
350 1.5.4.2 tls (void) detrunc(dep, dep->de_FileSize, 0, cred);
351 1.5.4.2 tls return (error);
352 1.5.4.2 tls }
353 1.5.4.2 tls }
354 1.5.4.2 tls
355 1.5.4.2 tls /*
356 1.5.4.2 tls * Zero extend file range; ubc_zerorange() uses ubc_alloc() and a
357 1.5.4.2 tls * memset(); we set the write size so ubc won't read in file data that
358 1.5.4.2 tls * is zero'd later.
359 1.5.4.2 tls */
360 1.5.4.2 tls dep->de_FileSize = length;
361 1.5.4.2 tls dep->de_flag |= DE_UPDATE|DE_MODIFIED;
362 1.5.4.2 tls return 0;
363 1.5.4.2 tls }
364