1 /* $OpenBSD: msdosfs_vnops.c,v 1.10 2023/08/11 04:51:36 guenther Exp $ */
2 /* $NetBSD: msdosfs_vnops.c,v 1.17 2016/01/30 09:59:27 mlelstv 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/types.h>
52 #include <sys/mman.h>
53 #include <fcntl.h>
54 #include <unistd.h>
55
56 #include "ffs/buf.h"
57
58 #include <msdosfs/bpb.h>
59 #include "msdos/direntry.h"
60 #include "msdos/denode.h"
61 #include "msdos/msdosfsmount.h"
62 #include "msdos/fat.h"
63
64 #include "makefs.h"
65 #include "msdos.h"
66
67 #ifdef MSDOSFS_DEBUG
68 #define DPRINTF(a) printf a
69 #else
70 #define DPRINTF(a)
71 #endif
72 /*
73 * Some general notes:
74 *
75 * In the ufs filesystem the inodes, superblocks, and indirect blocks are
76 * read/written using the mkfsvnode for the filesystem. Blocks that represent
77 * the contents of a file are read/written using the mkfsvnode for the file
78 * (including directories when they are read/written as files). This
79 * presents problems for the dos filesystem because data that should be in
80 * an inode (if dos had them) resides in the directory itself. Since we
81 * must update directory entries without the benefit of having the mkfsvnode
82 * for the directory we must use the mkfsvnode for the filesystem. This means
83 * that when a directory is actually read/written (via read, write, or
84 * readdir, or seek) we must use the mkfsvnode for the filesystem instead of
85 * the mkfsvnode for the directory as would happen in ufs. This is to insure we
86 * retrieve the correct block from the buffer cache since the hash value is
87 * based upon the mkfsvnode address and the desired block number.
88 */
89
90 static int msdosfs_wfile(const char *, struct denode *, fsnode *);
91
92 static void
msdosfs_times(struct msdosfsmount * pmp,struct denode * dep,const struct stat * st)93 msdosfs_times(struct msdosfsmount *pmp, struct denode *dep,
94 const struct stat *st)
95 {
96 unix2dostime(&st->st_atim, pmp->pm_minuteswest, &dep->de_ADate,
97 NULL, NULL);
98 unix2dostime(&st->st_mtim, pmp->pm_minuteswest, &dep->de_MDate,
99 &dep->de_MTime, NULL);
100 }
101
102 /*
103 * When we search a directory the blocks containing directory entries are
104 * read and examined. The directory entries contain information that would
105 * normally be in the inode of a unix filesystem. This means that some of
106 * a directory's contents may also be in memory resident denodes (sort of
107 * an inode). This can cause problems if we are searching while some other
108 * process is modifying a directory. To prevent one process from accessing
109 * incompletely modified directory information we depend upon being the
110 * sole owner of a directory block. bread/brelse provide this service.
111 * This being the case, when a process modifies a directory it must first
112 * acquire the disk block that contains the directory entry to be modified.
113 * Then update the disk block and the denode, and then write the disk block
114 * out to disk. This way disk blocks containing directory entries and in
115 * memory denode's will be in synch.
116 */
117 static int
msdosfs_findslot(struct denode * dp,struct componentname * cnp)118 msdosfs_findslot(struct denode *dp, struct componentname *cnp)
119 {
120 daddr_t bn;
121 int error;
122 int slotcount;
123 int slotoffset = 0;
124 int frcn;
125 u_long cluster;
126 int blkoff;
127 u_int diroff;
128 int blsize;
129 struct msdosfsmount *pmp;
130 struct mkfsbuf *bp = 0;
131 struct direntry *dep;
132 u_char dosfilename[12];
133 int wincnt = 1;
134 int chksum = -1, chksum_ok;
135 int olddos = 1;
136
137 pmp = dp->de_pmp;
138
139 switch (unix2dosfn(cnp->cn_nameptr, dosfilename, cnp->cn_namelen, 0)) {
140 case 0:
141 return (EINVAL);
142 case 1:
143 break;
144 case 2:
145 wincnt = winSlotCnt(cnp->cn_nameptr, cnp->cn_namelen) + 1;
146 break;
147 case 3:
148 olddos = 0;
149 wincnt = winSlotCnt(cnp->cn_nameptr, cnp->cn_namelen) + 1;
150 break;
151 }
152
153 if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
154 wincnt = 1;
155
156 /*
157 * Suppress search for slots unless creating
158 * file and at end of pathname, in which case
159 * we watch for a place to put the new file in
160 * case it doesn't already exist.
161 */
162 slotcount = 0;
163 DPRINTF(("%s(): dos filename: %s\n", __func__, dosfilename));
164 /*
165 * Search the directory pointed at by vdp for the name pointed at
166 * by cnp->cn_nameptr.
167 */
168 /*
169 * The outer loop ranges over the clusters that make up the
170 * directory. Note that the root directory is different from all
171 * other directories. It has a fixed number of blocks that are not
172 * part of the pool of allocatable clusters. So, we treat it a
173 * little differently. The root directory starts at "cluster" 0.
174 */
175 diroff = 0;
176 for (frcn = 0; diroff < dp->de_FileSize; frcn++) {
177 if ((error = pcbmap(dp, frcn, &bn, &cluster, &blsize)) != 0) {
178 if (error == E2BIG)
179 break;
180 return (error);
181 }
182 error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
183 0, &bp);
184 if (error) {
185 return (error);
186 }
187 for (blkoff = 0; blkoff < blsize;
188 blkoff += sizeof(struct direntry),
189 diroff += sizeof(struct direntry)) {
190 dep = (struct direntry *)((char *)bp->b_data + blkoff);
191 /*
192 * If the slot is empty and we are still looking
193 * for an empty then remember this one. If the
194 * slot is not empty then check to see if it
195 * matches what we are looking for. If the slot
196 * has never been filled with anything, then the
197 * remainder of the directory has never been used,
198 * so there is no point in searching it.
199 */
200 if (dep->deName[0] == SLOT_EMPTY ||
201 dep->deName[0] == SLOT_DELETED) {
202 /*
203 * Drop memory of previous long matches
204 */
205 chksum = -1;
206
207 if (slotcount < wincnt) {
208 slotcount++;
209 slotoffset = diroff;
210 }
211 if (dep->deName[0] == SLOT_EMPTY) {
212 brelse(bp, 0);
213 goto notfound;
214 }
215 } else {
216 /*
217 * If there wasn't enough space for our
218 * winentries, forget about the empty space
219 */
220 if (slotcount < wincnt)
221 slotcount = 0;
222
223 /*
224 * Check for Win95 long filename entry
225 */
226 if (dep->deAttributes == ATTR_WIN95) {
227 if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
228 continue;
229
230 chksum = winChkName(cnp->cn_nameptr,
231 cnp->cn_namelen,
232 (struct winentry *)dep,
233 chksum);
234 continue;
235 }
236
237 /*
238 * Ignore volume labels (anywhere, not just
239 * the root directory).
240 */
241 if (dep->deAttributes & ATTR_VOLUME) {
242 chksum = -1;
243 continue;
244 }
245
246 /*
247 * Check for a checksum or name match
248 */
249 chksum_ok = (chksum == winChksum(dep->deName));
250 if (!chksum_ok
251 && (!olddos || memcmp(dosfilename, dep->deName, 11))) {
252 chksum = -1;
253 continue;
254 }
255 DPRINTF(("%s(): match blkoff %d, diroff %d\n",
256 __func__, blkoff, diroff));
257 /*
258 * Remember where this directory
259 * entry came from for whoever did
260 * this lookup.
261 */
262 dp->de_fndoffset = diroff;
263 dp->de_fndcnt = 0;
264
265 return EEXIST;
266 }
267 } /* for (blkoff = 0; .... */
268 /*
269 * Release the buffer holding the directory cluster just
270 * searched.
271 */
272 brelse(bp, 0);
273 } /* for (frcn = 0; ; frcn++) */
274
275 notfound:
276 /*
277 * We hold no disk buffers at this point.
278 */
279
280 /*
281 * If we get here we didn't find the entry we were looking for. But
282 * that's ok if we are creating or renaming and are at the end of
283 * the pathname and the directory hasn't been removed.
284 */
285 DPRINTF(("%s(): refcnt %ld, slotcount %d, slotoffset %d\n",
286 __func__, dp->de_refcnt, slotcount, slotoffset));
287 /*
288 * Fixup the slot description to point to the place where
289 * we might put the new DOS direntry (putting the Win95
290 * long name entries before that)
291 */
292 if (!slotcount) {
293 slotcount = 1;
294 slotoffset = diroff;
295 }
296 if (wincnt > slotcount) {
297 slotoffset += sizeof(struct direntry) * (wincnt - slotcount);
298 }
299
300 /*
301 * Return an indication of where the new directory
302 * entry should be put.
303 */
304 dp->de_fndoffset = slotoffset;
305 dp->de_fndcnt = wincnt - 1;
306
307 /*
308 * We return with the directory locked, so that
309 * the parameters we set up above will still be
310 * valid if we actually decide to do a direnter().
311 * We return ni_vp == NULL to indicate that the entry
312 * does not currently exist; we leave a pointer to
313 * the (locked) directory inode in ndp->ni_dvp.
314 *
315 * NB - if the directory is unlocked, then this
316 * information cannot be used.
317 */
318 return 0;
319 }
320
321 /*
322 * Create a regular file. On entry the directory to contain the file being
323 * created is locked. We must release before we return.
324 */
325 struct denode *
msdosfs_mkfile(const char * path,struct denode * pdep,fsnode * node)326 msdosfs_mkfile(const char *path, struct denode *pdep, fsnode *node)
327 {
328 struct componentname cn;
329 struct denode ndirent;
330 struct denode *dep;
331 int error;
332 struct stat *st = &node->inode->st;
333 struct msdosfsmount *pmp = pdep->de_pmp;
334
335 cn.cn_nameptr = node->name;
336 cn.cn_namelen = strlen(node->name);
337
338 DPRINTF(("%s(name %s, mode 0%o size %zu)\n", __func__, node->name,
339 st->st_mode, (size_t)st->st_size));
340
341 /*
342 * If this is the root directory and there is no space left we
343 * can't do anything. This is because the root directory can not
344 * change size.
345 */
346 if (pdep->de_StartCluster == MSDOSFSROOT
347 && pdep->de_fndoffset >= pdep->de_FileSize) {
348 error = ENOSPC;
349 goto bad;
350 }
351
352 /*
353 * Create a directory entry for the file, then call createde() to
354 * have it installed. NOTE: DOS files are always executable. We
355 * use the absence of the owner write bit to make the file
356 * readonly.
357 */
358 memset(&ndirent, 0, sizeof(ndirent));
359 if ((error = uniqdosname(pdep, &cn, ndirent.de_Name)) != 0)
360 goto bad;
361
362 ndirent.de_Attributes = (st->st_mode & S_IWUSR) ?
363 ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY;
364 ndirent.de_StartCluster = 0;
365 ndirent.de_FileSize = 0;
366 ndirent.de_dev = pdep->de_dev;
367 ndirent.de_devvp = pdep->de_devvp;
368 ndirent.de_pmp = pdep->de_pmp;
369 ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
370 msdosfs_times(pmp, &ndirent, st);
371 if ((error = msdosfs_findslot(pdep, &cn)) != 0)
372 goto bad;
373 if ((error = createde(&ndirent, pdep, &dep, &cn)) != 0)
374 goto bad;
375 if ((error = msdosfs_wfile(path, dep, node)) != 0)
376 goto bad;
377 return dep;
378
379 bad:
380 errno = error;
381 return NULL;
382 }
383 static int
msdosfs_updatede(struct denode * dep)384 msdosfs_updatede(struct denode *dep)
385 {
386 struct mkfsbuf *bp;
387 struct direntry *dirp;
388 int error;
389
390 dep->de_flag &= ~DE_MODIFIED;
391 error = readde(dep, &bp, &dirp);
392 if (error)
393 return error;
394 DE_EXTERNALIZE(dirp, dep);
395 error = bwrite(bp);
396 return error;
397 }
398
399 /*
400 * Write data to a file or directory.
401 */
402 static int
msdosfs_wfile(const char * path,struct denode * dep,fsnode * node)403 msdosfs_wfile(const char *path, struct denode *dep, fsnode *node)
404 {
405 int error, fd;
406 size_t osize = dep->de_FileSize;
407 struct stat *st = &node->inode->st;
408 size_t nsize, offs;
409 struct msdosfsmount *pmp = dep->de_pmp;
410 struct mkfsbuf *bp;
411 char *dat;
412 u_long cn = 0;
413
414 error = 0; /* XXX: gcc/vax */
415 DPRINTF(("%s(diroff %lu, dirclust %lu, startcluster %lu)\n", __func__,
416 dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster));
417 if (st->st_size == 0)
418 return 0;
419
420 /* Don't bother to try to write files larger than the fs limit */
421 if (st->st_size > MSDOSFS_FILESIZE_MAX) {
422 errno = EFBIG;
423 return -1;
424 }
425
426 nsize = st->st_size;
427 DPRINTF(("%s(nsize=%zu, osize=%zu)\n", __func__, nsize, osize));
428 if (nsize > osize) {
429 if ((error = deextend(dep, nsize)) != 0) {
430 errno = error;
431 return -1;
432 }
433 if ((error = msdosfs_updatede(dep)) != 0) {
434 errno = error;
435 return -1;
436 }
437 }
438
439 if ((fd = open(path, O_RDONLY)) == -1)
440 err(1, "open %s", path);
441
442 if ((dat = mmap(0, nsize, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0))
443 == MAP_FAILED) {
444 DPRINTF(("%s: mmap %s %s", __func__, node->name,
445 strerror(errno)));
446 close(fd);
447 goto out;
448 }
449 close(fd);
450
451 for (offs = 0; offs < nsize;) {
452 int blsize, cpsize;
453 daddr_t bn;
454 u_long on = offs & pmp->pm_crbomask;
455 #ifdef HACK
456 cn = dep->de_StartCluster;
457 if (cn == MSDOSFSROOT) {
458 DPRINTF(("%s: bad lbn %lu", __func__, cn));
459 goto out;
460 }
461 bn = cntobn(pmp, cn);
462 blsize = pmp->pm_bpcluster;
463 #else
464 if ((error = pcbmap(dep, cn++, &bn, NULL, &blsize)) != 0) {
465 DPRINTF(("%s: pcbmap %lu", __func__, bn));
466 goto out;
467 }
468 #endif
469 DPRINTF(("%s(cn=%lu, bn=%llu/%llu, blsize=%d)\n", __func__,
470 cn, (unsigned long long)bn,
471 (unsigned long long)de_bn2kb(pmp, bn), blsize));
472 if ((error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
473 0, &bp)) != 0) {
474 DPRINTF(("bread %d\n", error));
475 goto out;
476 }
477 cpsize = MINIMUM((nsize - offs), blsize - on);
478 memcpy((char *)bp->b_data + on, dat + offs, cpsize);
479 bwrite(bp);
480 offs += cpsize;
481 }
482
483 munmap(dat, nsize);
484 return 0;
485 out:
486 munmap(dat, nsize);
487 return error;
488 }
489
490
491 static const struct {
492 struct direntry dot;
493 struct direntry dotdot;
494 } dosdirtemplate = {
495 { ". ", " ", /* the . entry */
496 ATTR_DIRECTORY, /* file attribute */
497 0, /* reserved */
498 0, { 0, 0 }, { 0, 0 }, /* create time & date */
499 { 0, 0 }, /* access date */
500 { 0, 0 }, /* high bits of start cluster */
501 { 210, 4 }, { 210, 4 }, /* modify time & date */
502 { 0, 0 }, /* startcluster */
503 { 0, 0, 0, 0 } /* filesize */
504 },
505 { ".. ", " ", /* the .. entry */
506 ATTR_DIRECTORY, /* file attribute */
507 0, /* reserved */
508 0, { 0, 0 }, { 0, 0 }, /* create time & date */
509 { 0, 0 }, /* access date */
510 { 0, 0 }, /* high bits of start cluster */
511 { 210, 4 }, { 210, 4 }, /* modify time & date */
512 { 0, 0 }, /* startcluster */
513 { 0, 0, 0, 0 } /* filesize */
514 }
515 };
516
517 struct denode *
msdosfs_mkdire(const char * path,struct denode * pdep,fsnode * node)518 msdosfs_mkdire(const char *path, struct denode *pdep, fsnode *node) {
519 struct denode ndirent;
520 struct denode *dep;
521 struct componentname cn;
522 struct stat *st = &node->inode->st;
523 struct msdosfsmount *pmp = pdep->de_pmp;
524 int error;
525 u_long newcluster, pcl, bn;
526 daddr_t lbn;
527 struct direntry *denp;
528 struct mkfsbuf *bp;
529
530 cn.cn_nameptr = node->name;
531 cn.cn_namelen = strlen(node->name);
532 /*
533 * If this is the root directory and there is no space left we
534 * can't do anything. This is because the root directory can not
535 * change size.
536 */
537 if (pdep->de_StartCluster == MSDOSFSROOT
538 && pdep->de_fndoffset >= pdep->de_FileSize) {
539 error = ENOSPC;
540 goto bad2;
541 }
542
543 /*
544 * Allocate a cluster to hold the about to be created directory.
545 */
546 error = clusteralloc(pmp, 0, 1, &newcluster, NULL);
547 if (error)
548 goto bad2;
549
550 memset(&ndirent, 0, sizeof(ndirent));
551 ndirent.de_pmp = pmp;
552 ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
553 msdosfs_times(pmp, &ndirent, st);
554
555 /*
556 * Now fill the cluster with the "." and ".." entries. And write
557 * the cluster to disk. This way it is there for the parent
558 * directory to be pointing at if there were a crash.
559 */
560 bn = cntobn(pmp, newcluster);
561 lbn = de_bn2kb(pmp, bn);
562 DPRINTF(("%s(newcluster %lu, bn=%lu, lbn=%lu)\n", __func__, newcluster,
563 bn, lbn));
564 /* always succeeds */
565 bp = getblk(pmp->pm_devvp, lbn, pmp->pm_bpcluster, 0, 0);
566 memset(bp->b_data, 0, pmp->pm_bpcluster);
567 memcpy(bp->b_data, &dosdirtemplate, sizeof dosdirtemplate);
568 denp = (struct direntry *)bp->b_data;
569 putushort(denp[0].deStartCluster, newcluster);
570 putushort(denp[0].deCDate, ndirent.de_CDate);
571 putushort(denp[0].deCTime, ndirent.de_CTime);
572 denp[0].deCTimeHundredth = ndirent.de_CHun;
573 putushort(denp[0].deADate, ndirent.de_ADate);
574 putushort(denp[0].deMDate, ndirent.de_MDate);
575 putushort(denp[0].deMTime, ndirent.de_MTime);
576 pcl = pdep->de_StartCluster;
577 DPRINTF(("%s(pcl %lu, rootdirblk=%lu)\n", __func__, pcl,
578 pmp->pm_rootdirblk));
579 if (FAT32(pmp) && pcl == pmp->pm_rootdirblk)
580 pcl = 0;
581 putushort(denp[1].deStartCluster, pcl);
582 putushort(denp[1].deCDate, ndirent.de_CDate);
583 putushort(denp[1].deCTime, ndirent.de_CTime);
584 denp[1].deCTimeHundredth = ndirent.de_CHun;
585 putushort(denp[1].deADate, ndirent.de_ADate);
586 putushort(denp[1].deMDate, ndirent.de_MDate);
587 putushort(denp[1].deMTime, ndirent.de_MTime);
588 if (FAT32(pmp)) {
589 putushort(denp[0].deHighClust, newcluster >> 16);
590 putushort(denp[1].deHighClust, pdep->de_StartCluster >> 16);
591 } else {
592 putushort(denp[0].deHighClust, 0);
593 putushort(denp[1].deHighClust, 0);
594 }
595
596 if ((error = bwrite(bp)) != 0)
597 goto bad;
598
599 /*
600 * Now build up a directory entry pointing to the newly allocated
601 * cluster. This will be written to an empty slot in the parent
602 * directory.
603 */
604 if ((error = uniqdosname(pdep, &cn, ndirent.de_Name)) != 0)
605 goto bad;
606
607 ndirent.de_Attributes = ATTR_DIRECTORY;
608 ndirent.de_StartCluster = newcluster;
609 ndirent.de_FileSize = 0;
610 ndirent.de_dev = pdep->de_dev;
611 ndirent.de_devvp = pdep->de_devvp;
612 ndirent.de_pmp = pdep->de_pmp;
613 if ((error = msdosfs_findslot(pdep, &cn)) != 0)
614 goto bad;
615 if ((error = createde(&ndirent, pdep, &dep, &cn)) != 0)
616 goto bad;
617 if ((error = msdosfs_updatede(dep)) != 0)
618 goto bad;
619 return dep;
620
621 bad:
622 clusterfree(pmp, newcluster, NULL);
623 bad2:
624 errno = error;
625 return NULL;
626 }
627