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