msdosfs_vnops.c revision 1.5 1 1.5 mbalmer /* $NetBSD: msdosfs_vnops.c,v 1.5 2013/01/27 10:07:23 mbalmer Exp $ */
2 1.1 christos
3 1.1 christos /*-
4 1.1 christos * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
5 1.1 christos * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
6 1.1 christos * All rights reserved.
7 1.1 christos * Original code by Paul Popelka (paulp (at) uts.amdahl.com) (see below).
8 1.1 christos *
9 1.1 christos * Redistribution and use in source and binary forms, with or without
10 1.1 christos * modification, are permitted provided that the following conditions
11 1.1 christos * are met:
12 1.1 christos * 1. Redistributions of source code must retain the above copyright
13 1.1 christos * notice, this list of conditions and the following disclaimer.
14 1.1 christos * 2. Redistributions in binary form must reproduce the above copyright
15 1.1 christos * notice, this list of conditions and the following disclaimer in the
16 1.1 christos * documentation and/or other materials provided with the distribution.
17 1.1 christos * 3. All advertising materials mentioning features or use of this software
18 1.1 christos * must display the following acknowledgement:
19 1.1 christos * This product includes software developed by TooLs GmbH.
20 1.1 christos * 4. The name of TooLs GmbH may not be used to endorse or promote products
21 1.1 christos * derived from this software without specific prior written permission.
22 1.1 christos *
23 1.1 christos * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
24 1.1 christos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 1.1 christos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 1.1 christos * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27 1.1 christos * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28 1.1 christos * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
29 1.1 christos * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30 1.1 christos * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
31 1.1 christos * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32 1.1 christos * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 1.1 christos */
34 1.1 christos /*
35 1.1 christos * Written by Paul Popelka (paulp (at) uts.amdahl.com)
36 1.1 christos *
37 1.1 christos * You can do anything you want with this software, just don't say you wrote
38 1.1 christos * it, and don't remove this notice.
39 1.1 christos *
40 1.1 christos * This software is provided "as is".
41 1.1 christos *
42 1.1 christos * The author supplies this software to be publicly redistributed on the
43 1.1 christos * understanding that the author is not responsible for the correct
44 1.1 christos * functioning of this software in any circumstances and is not liable for
45 1.1 christos * any damages caused by this software.
46 1.1 christos *
47 1.1 christos * October 1992
48 1.1 christos */
49 1.3 christos #if HAVE_NBTOOL_CONFIG_H
50 1.3 christos #include "nbtool_config.h"
51 1.3 christos #endif
52 1.1 christos
53 1.1 christos #include <sys/cdefs.h>
54 1.5 mbalmer __KERNEL_RCSID(0, "$NetBSD: msdosfs_vnops.c,v 1.5 2013/01/27 10:07:23 mbalmer Exp $");
55 1.1 christos
56 1.1 christos #include <sys/param.h>
57 1.1 christos #include <sys/mman.h>
58 1.1 christos #include <sys/mount.h>
59 1.1 christos #include <fcntl.h>
60 1.1 christos #include <unistd.h>
61 1.1 christos
62 1.1 christos #include <ffs/buf.h>
63 1.1 christos
64 1.1 christos #include <fs/msdosfs/bpb.h>
65 1.1 christos #include <fs/msdosfs/direntry.h>
66 1.1 christos #include <fs/msdosfs/denode.h>
67 1.1 christos #include <fs/msdosfs/msdosfsmount.h>
68 1.1 christos #include <fs/msdosfs/fat.h>
69 1.1 christos
70 1.1 christos #include "makefs.h"
71 1.1 christos #include "msdos.h"
72 1.1 christos
73 1.1 christos /*
74 1.1 christos * Some general notes:
75 1.1 christos *
76 1.1 christos * In the ufs filesystem the inodes, superblocks, and indirect blocks are
77 1.1 christos * read/written using the vnode for the filesystem. Blocks that represent
78 1.1 christos * the contents of a file are read/written using the vnode for the file
79 1.1 christos * (including directories when they are read/written as files). This
80 1.1 christos * presents problems for the dos filesystem because data that should be in
81 1.1 christos * an inode (if dos had them) resides in the directory itself. Since we
82 1.1 christos * must update directory entries without the benefit of having the vnode
83 1.1 christos * for the directory we must use the vnode for the filesystem. This means
84 1.1 christos * that when a directory is actually read/written (via read, write, or
85 1.1 christos * readdir, or seek) we must use the vnode for the filesystem instead of
86 1.1 christos * the vnode for the directory as would happen in ufs. This is to insure we
87 1.1 christos * retrieve the correct block from the buffer cache since the hash value is
88 1.1 christos * based upon the vnode address and the desired block number.
89 1.1 christos */
90 1.1 christos
91 1.1 christos static int msdosfs_wfile(const char *, struct denode *, fsnode *);
92 1.1 christos
93 1.1 christos static void
94 1.1 christos msdosfs_times(struct msdosfsmount *pmp, struct denode *dep,
95 1.1 christos const struct stat *st)
96 1.1 christos {
97 1.4 christos #ifndef HAVE_NBTOOL_CONFIG_H
98 1.3 christos struct timespec at = st->st_atimespec;
99 1.3 christos struct timespec mt = st->st_mtimespec;
100 1.3 christos #else
101 1.3 christos struct timespec at = { st->st_atime, 0 };
102 1.3 christos struct timespec mt = { st->st_mtime, 0 };
103 1.3 christos #endif
104 1.3 christos unix2dostime(&at, pmp->pm_gmtoff, &dep->de_ADate, NULL, NULL);
105 1.3 christos unix2dostime(&mt, pmp->pm_gmtoff, &dep->de_MDate, &dep->de_MTime, NULL);
106 1.1 christos }
107 1.1 christos
108 1.1 christos /*
109 1.1 christos * Create a regular file. On entry the directory to contain the file being
110 1.1 christos * created is locked. We must release before we return.
111 1.1 christos */
112 1.1 christos struct denode *
113 1.1 christos msdosfs_mkfile(const char *path, struct denode *pdep, fsnode *node)
114 1.1 christos {
115 1.1 christos struct componentname cn;
116 1.1 christos struct denode ndirent;
117 1.1 christos struct denode *dep;
118 1.1 christos int error;
119 1.1 christos struct stat *st = &node->inode->st;
120 1.1 christos struct msdosfsmount *pmp = pdep->de_pmp;
121 1.1 christos
122 1.1 christos cn.cn_nameptr = node->name;
123 1.1 christos cn.cn_namelen = strlen(node->name);
124 1.1 christos
125 1.1 christos #ifdef MSDOSFS_DEBUG
126 1.2 christos printf("msdosfs_create(name %s, mode 0%o size %zu\n", node->name,
127 1.2 christos st->st_mode, (size_t)st->st_size);
128 1.1 christos #endif
129 1.1 christos
130 1.1 christos /*
131 1.1 christos * If this is the root directory and there is no space left we
132 1.1 christos * can't do anything. This is because the root directory can not
133 1.1 christos * change size.
134 1.1 christos */
135 1.1 christos if (pdep->de_StartCluster == MSDOSFSROOT
136 1.1 christos && pdep->de_fndoffset >= pdep->de_FileSize) {
137 1.1 christos error = ENOSPC;
138 1.1 christos goto bad;
139 1.1 christos }
140 1.1 christos
141 1.1 christos /*
142 1.1 christos * Create a directory entry for the file, then call createde() to
143 1.1 christos * have it installed. NOTE: DOS files are always executable. We
144 1.1 christos * use the absence of the owner write bit to make the file
145 1.1 christos * readonly.
146 1.1 christos */
147 1.1 christos memset(&ndirent, 0, sizeof(ndirent));
148 1.1 christos if ((error = uniqdosname(pdep, &cn, ndirent.de_Name)) != 0)
149 1.1 christos goto bad;
150 1.1 christos
151 1.1 christos ndirent.de_Attributes = (st->st_mode & S_IWUSR) ?
152 1.1 christos ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY;
153 1.1 christos ndirent.de_StartCluster = 0;
154 1.1 christos ndirent.de_FileSize = 0;
155 1.1 christos ndirent.de_dev = pdep->de_dev;
156 1.1 christos ndirent.de_devvp = pdep->de_devvp;
157 1.1 christos ndirent.de_pmp = pdep->de_pmp;
158 1.1 christos ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
159 1.1 christos msdosfs_times(pmp, &ndirent, st);
160 1.1 christos if ((error = createde(&ndirent, pdep, &dep, &cn)) != 0)
161 1.1 christos goto bad;
162 1.1 christos if ((error = msdosfs_wfile(path, dep, node)) == -1)
163 1.1 christos goto bad;
164 1.1 christos return dep;
165 1.1 christos
166 1.1 christos bad:
167 1.1 christos errno = error;
168 1.1 christos return NULL;
169 1.1 christos }
170 1.1 christos
171 1.1 christos /*
172 1.1 christos * Write data to a file or directory.
173 1.1 christos */
174 1.1 christos static int
175 1.1 christos msdosfs_wfile(const char *path, struct denode *dep, fsnode *node)
176 1.1 christos {
177 1.1 christos int error, fd;
178 1.1 christos size_t osize = dep->de_FileSize;
179 1.1 christos struct stat *st = &node->inode->st;
180 1.5 mbalmer off_t nsize = st->st_size;
181 1.1 christos size_t offs = 0;
182 1.1 christos struct msdosfsmount *pmp = dep->de_pmp;
183 1.1 christos struct buf *bp;
184 1.1 christos char *dat;
185 1.3 christos u_long cn;
186 1.1 christos
187 1.1 christos #ifdef MSDOSFS_DEBUG
188 1.1 christos printf("msdosfs_write(): diroff %lu, dirclust %lu, startcluster %lu\n",
189 1.1 christos dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster);
190 1.1 christos #endif
191 1.1 christos if (nsize == 0)
192 1.1 christos return 0;
193 1.1 christos
194 1.1 christos /* Don't bother to try to write files larger than the fs limit */
195 1.1 christos if (nsize > MSDOSFS_FILESIZE_MAX) {
196 1.1 christos errno = EFBIG;
197 1.1 christos return -1;
198 1.1 christos }
199 1.1 christos
200 1.1 christos if (nsize > osize) {
201 1.1 christos if ((error = deextend(dep, nsize, NULL)) != 0) {
202 1.1 christos errno = error;
203 1.1 christos return -1;
204 1.1 christos }
205 1.1 christos }
206 1.1 christos
207 1.1 christos if ((fd = open(path, O_RDONLY)) == -1)
208 1.1 christos err(1, "open %s", path);
209 1.1 christos
210 1.5 mbalmer if ((dat = mmap(0, (size_t)nsize, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0))
211 1.2 christos == MAP_FAILED)
212 1.1 christos err(1, "mmap %s", node->name);
213 1.1 christos close(fd);
214 1.1 christos
215 1.3 christos for (cn = 0;; cn++) {
216 1.1 christos int blsize, cpsize;
217 1.1 christos daddr_t bn;
218 1.1 christos if (pcbmap(dep, cn, &bn, 0, &blsize)) {
219 1.1 christos fprintf(stderr, "pcbmap %ld\n", cn);
220 1.1 christos goto out;
221 1.1 christos }
222 1.1 christos
223 1.1 christos if (bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize, NULL,
224 1.1 christos 0, &bp)) {
225 1.1 christos fprintf(stderr, "bread\n");
226 1.1 christos }
227 1.1 christos cpsize = MIN((int)(nsize - offs), blsize);
228 1.1 christos memcpy(bp->b_data, dat + offs, cpsize);
229 1.1 christos bwrite(bp);
230 1.1 christos brelse(bp, 0);
231 1.1 christos offs += blsize;
232 1.1 christos if (offs >= nsize)
233 1.1 christos break;
234 1.1 christos }
235 1.1 christos
236 1.1 christos munmap(dat, nsize);
237 1.1 christos return 0;
238 1.1 christos out:
239 1.1 christos munmap(dat, nsize);
240 1.1 christos return -1;
241 1.1 christos }
242 1.1 christos
243 1.1 christos
244 1.1 christos static const struct {
245 1.1 christos struct direntry dot;
246 1.1 christos struct direntry dotdot;
247 1.1 christos } dosdirtemplate = {
248 1.1 christos { ". ", " ", /* the . entry */
249 1.1 christos ATTR_DIRECTORY, /* file attribute */
250 1.1 christos 0, /* reserved */
251 1.1 christos 0, { 0, 0 }, { 0, 0 }, /* create time & date */
252 1.1 christos { 0, 0 }, /* access date */
253 1.1 christos { 0, 0 }, /* high bits of start cluster */
254 1.1 christos { 210, 4 }, { 210, 4 }, /* modify time & date */
255 1.1 christos { 0, 0 }, /* startcluster */
256 1.1 christos { 0, 0, 0, 0 } /* filesize */
257 1.1 christos },
258 1.1 christos { ".. ", " ", /* the .. entry */
259 1.1 christos ATTR_DIRECTORY, /* file attribute */
260 1.1 christos 0, /* reserved */
261 1.1 christos 0, { 0, 0 }, { 0, 0 }, /* create time & date */
262 1.1 christos { 0, 0 }, /* access date */
263 1.1 christos { 0, 0 }, /* high bits of start cluster */
264 1.1 christos { 210, 4 }, { 210, 4 }, /* modify time & date */
265 1.1 christos { 0, 0 }, /* startcluster */
266 1.1 christos { 0, 0, 0, 0 } /* filesize */
267 1.1 christos }
268 1.1 christos };
269 1.1 christos
270 1.1 christos struct denode *
271 1.1 christos msdosfs_mkdire(const char *path, struct denode *pdep, fsnode *node) {
272 1.1 christos struct denode ndirent;
273 1.1 christos struct denode *dep;
274 1.1 christos struct componentname cn;
275 1.1 christos struct stat *st = &node->inode->st;
276 1.1 christos struct msdosfsmount *pmp = pdep->de_pmp;
277 1.1 christos int error;
278 1.1 christos u_long newcluster, pcl, bn;
279 1.1 christos daddr_t lbn;
280 1.1 christos struct direntry *denp;
281 1.1 christos struct buf *bp;
282 1.1 christos
283 1.1 christos cn.cn_nameptr = node->name;
284 1.1 christos cn.cn_namelen = strlen(node->name);
285 1.1 christos /*
286 1.1 christos * If this is the root directory and there is no space left we
287 1.1 christos * can't do anything. This is because the root directory can not
288 1.1 christos * change size.
289 1.1 christos */
290 1.1 christos if (pdep->de_StartCluster == MSDOSFSROOT
291 1.1 christos && pdep->de_fndoffset >= pdep->de_FileSize) {
292 1.1 christos error = ENOSPC;
293 1.1 christos goto bad2;
294 1.1 christos }
295 1.1 christos
296 1.1 christos /*
297 1.1 christos * Allocate a cluster to hold the about to be created directory.
298 1.1 christos */
299 1.1 christos error = clusteralloc(pmp, 0, 1, &newcluster, NULL);
300 1.1 christos if (error)
301 1.1 christos goto bad2;
302 1.1 christos
303 1.1 christos memset(&ndirent, 0, sizeof(ndirent));
304 1.1 christos ndirent.de_pmp = pmp;
305 1.1 christos ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
306 1.1 christos msdosfs_times(pmp, &ndirent, st);
307 1.1 christos
308 1.1 christos /*
309 1.1 christos * Now fill the cluster with the "." and ".." entries. And write
310 1.1 christos * the cluster to disk. This way it is there for the parent
311 1.1 christos * directory to be pointing at if there were a crash.
312 1.1 christos */
313 1.1 christos bn = cntobn(pmp, newcluster);
314 1.1 christos lbn = de_bn2kb(pmp, bn);
315 1.1 christos /* always succeeds */
316 1.1 christos bp = getblk(pmp->pm_devvp, lbn, pmp->pm_bpcluster, 0, 0);
317 1.1 christos memset(bp->b_data, 0, pmp->pm_bpcluster);
318 1.1 christos memcpy(bp->b_data, &dosdirtemplate, sizeof dosdirtemplate);
319 1.1 christos denp = (struct direntry *)bp->b_data;
320 1.1 christos putushort(denp[0].deStartCluster, newcluster);
321 1.1 christos putushort(denp[0].deCDate, ndirent.de_CDate);
322 1.1 christos putushort(denp[0].deCTime, ndirent.de_CTime);
323 1.1 christos denp[0].deCHundredth = ndirent.de_CHun;
324 1.1 christos putushort(denp[0].deADate, ndirent.de_ADate);
325 1.1 christos putushort(denp[0].deMDate, ndirent.de_MDate);
326 1.1 christos putushort(denp[0].deMTime, ndirent.de_MTime);
327 1.1 christos pcl = pdep->de_StartCluster;
328 1.1 christos if (FAT32(pmp) && pcl == pmp->pm_rootdirblk)
329 1.1 christos pcl = 0;
330 1.1 christos putushort(denp[1].deStartCluster, pcl);
331 1.1 christos putushort(denp[1].deCDate, ndirent.de_CDate);
332 1.1 christos putushort(denp[1].deCTime, ndirent.de_CTime);
333 1.1 christos denp[1].deCHundredth = ndirent.de_CHun;
334 1.1 christos putushort(denp[1].deADate, ndirent.de_ADate);
335 1.1 christos putushort(denp[1].deMDate, ndirent.de_MDate);
336 1.1 christos putushort(denp[1].deMTime, ndirent.de_MTime);
337 1.1 christos if (FAT32(pmp)) {
338 1.1 christos putushort(denp[0].deHighClust, newcluster >> 16);
339 1.1 christos putushort(denp[1].deHighClust, pdep->de_StartCluster >> 16);
340 1.1 christos } else {
341 1.1 christos putushort(denp[0].deHighClust, 0);
342 1.1 christos putushort(denp[1].deHighClust, 0);
343 1.1 christos }
344 1.1 christos
345 1.1 christos if ((error = bwrite(bp)) != 0)
346 1.1 christos goto bad;
347 1.1 christos
348 1.1 christos /*
349 1.1 christos * Now build up a directory entry pointing to the newly allocated
350 1.1 christos * cluster. This will be written to an empty slot in the parent
351 1.1 christos * directory.
352 1.1 christos */
353 1.1 christos if ((error = uniqdosname(pdep, &cn, ndirent.de_Name)) != 0)
354 1.1 christos goto bad;
355 1.1 christos
356 1.1 christos ndirent.de_Attributes = ATTR_DIRECTORY;
357 1.1 christos ndirent.de_StartCluster = newcluster;
358 1.1 christos ndirent.de_FileSize = 0;
359 1.1 christos ndirent.de_dev = pdep->de_dev;
360 1.1 christos ndirent.de_devvp = pdep->de_devvp;
361 1.1 christos if ((error = createde(&ndirent, pdep, &dep, &cn)) != 0)
362 1.1 christos goto bad;
363 1.1 christos return dep;
364 1.1 christos
365 1.1 christos bad:
366 1.1 christos clusterfree(pmp, newcluster, NULL);
367 1.1 christos bad2:
368 1.1 christos errno = error;
369 1.1 christos return NULL;
370 1.1 christos }
371