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