1 /* $NetBSD: msdosfs_lookup.c,v 1.37 1997/11/17 15:36:54 ws Exp $ */
2
3 /*-
4 * SPDX-License-Identifier: BSD-4-Clause
5 *
6 * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
7 * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
8 * All rights reserved.
9 * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by TooLs GmbH.
22 * 4. The name of TooLs GmbH may not be used to endorse or promote products
23 * derived from this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
26 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
31 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
32 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
33 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
34 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 */
36 /*-
37 * Written by Paul Popelka (paulp@uts.amdahl.com)
38 *
39 * You can do anything you want with this software, just don't say you wrote
40 * it, and don't remove this notice.
41 *
42 * This software is provided "as is".
43 *
44 * The author supplies this software to be publicly redistributed on the
45 * understanding that the author is not responsible for the correct
46 * functioning of this software in any circumstances and is not liable for
47 * any damages caused by this software.
48 *
49 * October 1992
50 */
51
52 #include <sys/param.h>
53 #include <sys/errno.h>
54
55 #include <stdbool.h>
56 #include <stdio.h>
57 #include <string.h>
58
59 #include <fs/msdosfs/bpb.h>
60 #include "msdos/denode.h"
61 #include <fs/msdosfs/fat.h>
62 #include <fs/msdosfs/msdosfsmount.h>
63
64 #include "makefs.h"
65 #include "msdos.h"
66
67 /*
68 * dep - directory entry to copy into the directory
69 * ddep - directory to add to
70 * depp - return the address of the denode for the created directory entry
71 * if depp != 0
72 * cnp - componentname needed for Win95 long filenames
73 */
74 int
createde(struct denode * dep,struct denode * ddep,struct denode ** depp,struct componentname * cnp)75 createde(struct denode *dep, struct denode *ddep, struct denode **depp,
76 struct componentname *cnp)
77 {
78 int error;
79 u_long dirclust, diroffset;
80 struct direntry *ndep;
81 struct msdosfsmount *pmp = ddep->de_pmp;
82 struct m_buf *bp;
83 daddr_t bn;
84 int blsize;
85
86 MSDOSFS_DPRINTF(("createde(dep %p, ddep %p, depp %p, cnp %p)\n",
87 dep, ddep, depp, cnp));
88
89 /*
90 * If no space left in the directory then allocate another cluster
91 * and chain it onto the end of the file. There is one exception
92 * to this. That is, if the root directory has no more space it
93 * can NOT be expanded. extendfile() checks for and fails attempts
94 * to extend the root directory. We just return an error in that
95 * case.
96 */
97 if (ddep->de_fndoffset >= ddep->de_FileSize) {
98 diroffset = ddep->de_fndoffset + sizeof(struct direntry)
99 - ddep->de_FileSize;
100 dirclust = de_clcount(pmp, diroffset);
101 error = m_extendfile(ddep, dirclust, 0, 0, DE_CLEAR);
102 if (error) {
103 (void)detrunc(ddep, ddep->de_FileSize, 0, NULL);
104 return error;
105 }
106
107 /*
108 * Update the size of the directory
109 */
110 ddep->de_FileSize += de_cn2off(pmp, dirclust);
111 }
112
113 /*
114 * We just read in the cluster with space. Copy the new directory
115 * entry in. Then write it to disk. NOTE: DOS directories
116 * do not get smaller as clusters are emptied.
117 */
118 error = pcbmap(ddep, de_cluster(pmp, ddep->de_fndoffset),
119 &bn, &dirclust, &blsize);
120 if (error)
121 return error;
122 diroffset = ddep->de_fndoffset;
123 if (dirclust != MSDOSFSROOT)
124 diroffset &= pmp->pm_crbomask;
125 if ((error = bread((void *)pmp->pm_devvp, bn, blsize, NOCRED,
126 &bp)) != 0) {
127 return error;
128 }
129 ndep = bptoep(pmp, bp, ddep->de_fndoffset);
130
131 DE_EXTERNALIZE(ndep, dep);
132
133 /*
134 * Now write the Win95 long name
135 */
136 if (ddep->de_fndcnt > 0) {
137 uint8_t chksum = winChksum(ndep->deName);
138 const u_char *un = (const u_char *)cnp->cn_nameptr;
139 int unlen = cnp->cn_namelen;
140 int cnt = 1;
141
142 while (--ddep->de_fndcnt >= 0) {
143 if (!(ddep->de_fndoffset & pmp->pm_crbomask)) {
144 if ((error = bwrite(bp)) != 0)
145 return error;
146
147 ddep->de_fndoffset -= sizeof(struct direntry);
148 error = pcbmap(ddep,
149 de_cluster(pmp,
150 ddep->de_fndoffset),
151 &bn, 0, &blsize);
152 if (error)
153 return error;
154
155 error = bread((void *)pmp->pm_devvp, bn, blsize,
156 NOCRED, &bp);
157 if (error) {
158 return error;
159 }
160 ndep = bptoep(pmp, bp, ddep->de_fndoffset);
161 } else {
162 ndep--;
163 ddep->de_fndoffset -= sizeof(struct direntry);
164 }
165 if (!unix2winfn(un, unlen, (struct winentry *)ndep,
166 cnt++, chksum))
167 break;
168 }
169 }
170
171 if ((error = bwrite(bp)) != 0)
172 return error;
173
174 /*
175 * If they want us to return with the denode gotten.
176 */
177 if (depp) {
178 if (dep->de_Attributes & ATTR_DIRECTORY) {
179 dirclust = dep->de_StartCluster;
180 if (FAT32(pmp) && dirclust == pmp->pm_rootdirblk)
181 dirclust = MSDOSFSROOT;
182 if (dirclust == MSDOSFSROOT)
183 diroffset = MSDOSFSROOT_OFS;
184 else
185 diroffset = 0;
186 }
187 return deget(pmp, dirclust, diroffset, 0, depp);
188 }
189
190 return 0;
191 }
192
193 /*
194 * Read in the disk block containing the directory entry (dirclu, dirofs)
195 * and return the address of the buf header, and the address of the
196 * directory entry within the block.
197 */
198 int
m_readep(struct msdosfsmount * pmp,u_long dirclust,u_long diroffset,struct m_buf ** bpp,struct direntry ** epp)199 m_readep(struct msdosfsmount *pmp, u_long dirclust, u_long diroffset,
200 struct m_buf **bpp, struct direntry **epp)
201 {
202 int error;
203 daddr_t bn;
204 int blsize;
205
206 blsize = pmp->pm_bpcluster;
207 if (dirclust == MSDOSFSROOT
208 && de_blk(pmp, diroffset + blsize) > pmp->pm_rootdirsize)
209 blsize = de_bn2off(pmp, pmp->pm_rootdirsize) & pmp->pm_crbomask;
210 bn = detobn(pmp, dirclust, diroffset);
211 if ((error = bread((void *)pmp->pm_devvp, bn, blsize, NOCRED,
212 bpp)) != 0) {
213 *bpp = NULL;
214 return (error);
215 }
216 if (epp)
217 *epp = bptoep(pmp, *bpp, diroffset);
218 return (0);
219 }
220
221 /*
222 * Read in the disk block containing the directory entry dep came from and
223 * return the address of the buf header, and the address of the directory
224 * entry within the block.
225 */
226 int
m_readde(struct denode * dep,struct m_buf ** bpp,struct direntry ** epp)227 m_readde(struct denode *dep, struct m_buf **bpp, struct direntry **epp)
228 {
229
230 return (m_readep(dep->de_pmp, dep->de_dirclust, dep->de_diroffset,
231 bpp, epp));
232 }
233
234 /*
235 * Create a unique DOS name in dvp
236 */
237 int
uniqdosname(struct denode * dep,struct componentname * cnp,u_char * cp)238 uniqdosname(struct denode *dep, struct componentname *cnp, u_char *cp)
239 {
240 struct msdosfsmount *pmp = dep->de_pmp;
241 struct direntry *dentp;
242 int gen;
243 int blsize;
244 u_long cn;
245 daddr_t bn;
246 struct m_buf *bp;
247 int error;
248
249 if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
250 return (unix2dosfn((const u_char *)cnp->cn_nameptr, cp,
251 cnp->cn_namelen, 0) ? 0 : EINVAL);
252
253 for (gen = 1;; gen++) {
254 /*
255 * Generate DOS name with generation number
256 */
257 if (!unix2dosfn((const u_char *)cnp->cn_nameptr, cp,
258 cnp->cn_namelen, gen))
259 return gen == 1 ? EINVAL : EEXIST;
260
261 /*
262 * Now look for a dir entry with this exact name
263 */
264 for (cn = error = 0; !error; cn++) {
265 if ((error = pcbmap(dep, cn, &bn, 0, &blsize)) != 0) {
266 if (error == E2BIG) /* EOF reached and not found */
267 return 0;
268 return error;
269 }
270 error = bread((void *)pmp->pm_devvp, bn, blsize,
271 NOCRED, &bp);
272 if (error) {
273 return error;
274 }
275 for (dentp = (struct direntry *)bp->b_data;
276 (char *)dentp < bp->b_data + blsize;
277 dentp++) {
278 if (dentp->deName[0] == SLOT_EMPTY) {
279 /*
280 * Last used entry and not found
281 */
282 brelse(bp);
283 return 0;
284 }
285 /*
286 * Ignore volume labels and Win95 entries
287 */
288 if (dentp->deAttributes & ATTR_VOLUME)
289 continue;
290 if (!bcmp(dentp->deName, cp, 11)) {
291 error = EEXIST;
292 break;
293 }
294 }
295 brelse(bp);
296 }
297 }
298 }
299