xref: /openbsd-src/usr.sbin/makefs/msdos/msdosfs_denode.c (revision 99fd087599a8791921855f21bd7e36130f39aadc)
1 /*	$OpenBSD: msdosfs_denode.c,v 1.6 2016/10/22 22:20:24 natano 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 #include <sys/param.h>
52 
53 #include "ffs/buf.h"
54 
55 #include <msdosfs/bpb.h>
56 #include "msdos/msdosfsmount.h"
57 #include "msdos/direntry.h"
58 #include "msdos/denode.h"
59 #include "msdos/fat.h"
60 
61 #include <util.h>
62 
63 #include "makefs.h"
64 
65 /*
66  * If deget() succeeds it returns with the gotten denode locked().
67  *
68  * pmp	     - address of msdosfsmount structure of the filesystem containing
69  *	       the denode of interest.  The pm_dev field and the address of
70  *	       the msdosfsmount structure are used.
71  * dirclust  - which cluster bp contains, if dirclust is 0 (root directory)
72  *	       diroffset is relative to the beginning of the root directory,
73  *	       otherwise it is cluster relative.
74  * diroffset - offset past begin of cluster of denode we want
75  * depp	     - returns the address of the gotten denode.
76  */
77 int
78 deget(struct msdosfsmount *pmp, u_long dirclust, u_long diroffset,
79     struct denode **depp)
80 	/* pmp:	 so we know the maj/min number */
81 	/* dirclust:		 cluster this dir entry came from */
82 	/* diroffset:		 index of entry within the cluster */
83 	/* depp:		 returns the addr of the gotten denode */
84 {
85 	int error;
86 	struct direntry *direntptr;
87 	struct denode *ldep;
88 	struct mkfsbuf *bp;
89 
90 #ifdef MSDOSFS_DEBUG
91 	printf("deget(pmp %p, dirclust %lu, diroffset %lx, depp %p)\n",
92 	    pmp, dirclust, diroffset, depp);
93 #endif
94 
95 	/*
96 	 * On FAT32 filesystems, root is a (more or less) normal
97 	 * directory
98 	 */
99 	if (FAT32(pmp) && dirclust == MSDOSFSROOT)
100 		dirclust = pmp->pm_rootdirblk;
101 
102 	ldep = ecalloc(1, sizeof(*ldep));
103 	ldep->de_mkfsvnode = NULL;
104 	ldep->de_flag = 0;
105 	ldep->de_devvp = 0;
106 	ldep->de_lockf = 0;
107 	ldep->de_dev = pmp->pm_dev;
108 	ldep->de_dirclust = dirclust;
109 	ldep->de_diroffset = diroffset;
110 	ldep->de_pmp = pmp;
111 	ldep->de_devvp = pmp->pm_devvp;
112 	ldep->de_refcnt = 1;
113 	fc_purge(ldep, 0);
114 	/*
115 	 * Copy the directory entry into the denode area of the mkfsvnode.
116 	 */
117 	if ((dirclust == MSDOSFSROOT
118 	     || (FAT32(pmp) && dirclust == pmp->pm_rootdirblk))
119 	    && diroffset == MSDOSFSROOT_OFS) {
120 		/*
121 		 * Directory entry for the root directory. There isn't one,
122 		 * so we manufacture one. We should probably rummage
123 		 * through the root directory and find a label entry (if it
124 		 * exists), and then use the time and date from that entry
125 		 * as the time and date for the root denode.
126 		 */
127 		ldep->de_mkfsvnode = (struct mkfsvnode *)-1;
128 
129 		ldep->de_Attributes = ATTR_DIRECTORY;
130 		if (FAT32(pmp))
131 			ldep->de_StartCluster = pmp->pm_rootdirblk;
132 			/* de_FileSize will be filled in further down */
133 		else {
134 			ldep->de_StartCluster = MSDOSFSROOT;
135 			ldep->de_FileSize = pmp->pm_rootdirsize * pmp->pm_BytesPerSec;
136 		}
137 		/*
138 		 * fill in time and date so that dos2unixtime() doesn't
139 		 * spit up when called from msdosfs_getattr() with root
140 		 * denode
141 		 */
142 		ldep->de_CHun = 0;
143 		ldep->de_CTime = 0x0000;	/* 00:00:00	 */
144 		ldep->de_CDate = (0 << DD_YEAR_SHIFT) | (1 << DD_MONTH_SHIFT)
145 		    | (1 << DD_DAY_SHIFT);
146 		/* Jan 1, 1980	 */
147 		ldep->de_ADate = ldep->de_CDate;
148 		ldep->de_MTime = ldep->de_CTime;
149 		ldep->de_MDate = ldep->de_CDate;
150 		/* leave the other fields as garbage */
151 	} else {
152 		error = readep(pmp, dirclust, diroffset, &bp, &direntptr);
153 		if (error) {
154 			ldep->de_devvp = NULL;
155 			ldep->de_Name[0] = SLOT_DELETED;
156 			return (error);
157 		}
158 		DE_INTERNALIZE(ldep, direntptr);
159 		brelse(bp, 0);
160 	}
161 
162 	/*
163 	 * Fill in a few fields of the mkfsvnode and finish filling in the
164 	 * denode.  Then return the address of the found denode.
165 	 */
166 	if (ldep->de_Attributes & ATTR_DIRECTORY) {
167 		/*
168 		 * Since DOS directory entries that describe directories
169 		 * have 0 in the filesize field, we take this opportunity
170 		 * to find out the length of the directory and plug it into
171 		 * the denode structure.
172 		 */
173 		u_long size;
174 
175 		if (ldep->de_StartCluster != MSDOSFSROOT) {
176 			error = pcbmap(ldep, CLUST_END, 0, &size, 0);
177 			if (error == E2BIG) {
178 				ldep->de_FileSize = de_cn2off(pmp, size);
179 				error = 0;
180 			} else
181 				printf("deget(): pcbmap returned %d\n", error);
182 		}
183 	}
184 	*depp = ldep;
185 	return (0);
186 }
187 
188 /*
189  * Truncate the file described by dep to the length specified by length.
190  */
191 int
192 detrunc(struct denode *dep, u_long length, int flags)
193 {
194 	int error;
195 	int allerror = 0;
196 	u_long eofentry;
197 	u_long chaintofree = 0;
198 	daddr_t bn, lastblock;
199 	int boff;
200 	int isadir = dep->de_Attributes & ATTR_DIRECTORY;
201 	struct mkfsbuf *bp;
202 	struct msdosfsmount *pmp = dep->de_pmp;
203 
204 #ifdef MSDOSFS_DEBUG
205 	printf("detrunc(): file %s, length %lu, flags %x\n", dep->de_Name, length, flags);
206 #endif
207 
208 	/*
209 	 * Disallow attempts to truncate the root directory since it is of
210 	 * fixed size.  That's just the way dos filesystems are.  We use
211 	 * the VROOT bit in the mkfsvnode because checking for the directory
212 	 * bit and a startcluster of 0 in the denode is not adequate to
213 	 * recognize the root directory at this point in a file or
214 	 * directory's life.
215 	 */
216 	if (dep->de_mkfsvnode != NULL && !FAT32(pmp)) {
217 		printf("detrunc(): can't truncate root directory, clust %ld, offset %ld\n",
218 		    dep->de_dirclust, dep->de_diroffset);
219 		return (EINVAL);
220 	}
221 
222 	if (dep->de_FileSize < length)
223 		return (deextend(dep, length));
224 	lastblock = de_clcount(pmp, length) - 1;
225 
226 	/*
227 	 * If the desired length is 0 then remember the starting cluster of
228 	 * the file and set the StartCluster field in the directory entry
229 	 * to 0.  If the desired length is not zero, then get the number of
230 	 * the last cluster in the shortened file.  Then get the number of
231 	 * the first cluster in the part of the file that is to be freed.
232 	 * Then set the next cluster pointer in the last cluster of the
233 	 * file to CLUST_EOFE.
234 	 */
235 	if (length == 0) {
236 		chaintofree = dep->de_StartCluster;
237 		dep->de_StartCluster = 0;
238 		eofentry = ~0;
239 	} else {
240 		error = pcbmap(dep, lastblock, 0, &eofentry, 0);
241 		if (error) {
242 #ifdef MSDOSFS_DEBUG
243 			printf("detrunc(): pcbmap fails %d\n", error);
244 #endif
245 			return (error);
246 		}
247 	}
248 
249 	/*
250 	 * If the new length is not a multiple of the cluster size then we
251 	 * must zero the tail end of the new last cluster in case it
252 	 * becomes part of the file again because of a seek.
253 	 */
254 	if ((boff = length & pmp->pm_crbomask) != 0) {
255 		if (isadir) {
256 			bn = cntobn(pmp, eofentry);
257 			error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn),
258 			    pmp->pm_bpcluster, B_MODIFY, &bp);
259 			if (error) {
260 #ifdef MSDOSFS_DEBUG
261 				printf("detrunc(): bread fails %d\n", error);
262 #endif
263 				return (error);
264 			}
265 			memset((char *)bp->b_data + boff, 0,
266 			    pmp->pm_bpcluster - boff);
267 			if (flags & IO_SYNC)
268 				bwrite(bp);
269 			else
270 				bdwrite(bp);
271 		}
272 	}
273 
274 	/*
275 	 * Write out the updated directory entry.  Even if the update fails
276 	 * we free the trailing clusters.
277 	 */
278 	dep->de_FileSize = length;
279 	if (!isadir)
280 		dep->de_flag |= DE_UPDATE|DE_MODIFIED;
281 #ifdef MSDOSFS_DEBUG
282 	printf("detrunc(): allerror %d, eofentry %lu\n",
283 	       allerror, eofentry);
284 #endif
285 
286 	/*
287 	 * If we need to break the cluster chain for the file then do it
288 	 * now.
289 	 */
290 	if (eofentry != (u_long)~0) {
291 		error = fatentry(FAT_GET_AND_SET, pmp, eofentry,
292 				 &chaintofree, CLUST_EOFE);
293 		if (error) {
294 #ifdef MSDOSFS_DEBUG
295 			printf("detrunc(): fatentry errors %d\n", error);
296 #endif
297 			return (error);
298 		}
299 	}
300 
301 	/*
302 	 * Now free the clusters removed from the file because of the
303 	 * truncation.
304 	 */
305 	if (chaintofree != 0 && !MSDOSFSEOF(chaintofree, pmp->pm_fatmask))
306 		freeclusterchain(pmp, chaintofree);
307 
308 	return (allerror);
309 }
310 
311 /*
312  * Extend the file described by dep to length specified by length.
313  */
314 int
315 deextend(struct denode *dep, u_long length)
316 {
317 	struct msdosfsmount *pmp = dep->de_pmp;
318 	u_long count;
319 	int error;
320 
321 	/*
322 	 * The root of a DOS filesystem cannot be extended.
323 	 */
324 	if (dep->de_mkfsvnode != NULL && !FAT32(pmp))
325 		return EINVAL;
326 
327 	/*
328 	 * Directories cannot be extended.
329 	 */
330 	if (dep->de_Attributes & ATTR_DIRECTORY)
331 		return EISDIR;
332 
333 	if (length <= dep->de_FileSize)
334 		return E2BIG;
335 
336 	/*
337 	 * Compute the number of clusters to allocate.
338 	 */
339 	count = de_clcount(pmp, length) - de_clcount(pmp, dep->de_FileSize);
340 	if (count > 0) {
341 		if (count > pmp->pm_freeclustercount)
342 			return (ENOSPC);
343 		error = extendfile(dep, count, NULL, NULL, DE_CLEAR);
344 		if (error) {
345 			/* truncate the added clusters away again */
346 			(void) detrunc(dep, dep->de_FileSize, 0);
347 			return (error);
348 		}
349 	}
350 
351 	/*
352 	 * Zero extend file range; ubc_zerorange() uses ubc_alloc() and a
353 	 * memset(); we set the write size so ubc won't read in file data that
354 	 * is zero'd later.
355 	 */
356 	dep->de_FileSize = length;
357 	dep->de_flag |= DE_UPDATE|DE_MODIFIED;
358 	return 0;
359 }
360