msdosfs_vnops.c revision 1.1 1 1.1 christos /* $NetBSD: msdosfs_vnops.c,v 1.1 2013/01/26 00:20:40 christos 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.1 christos
50 1.1 christos #include <sys/cdefs.h>
51 1.1 christos __KERNEL_RCSID(0, "$NetBSD: msdosfs_vnops.c,v 1.1 2013/01/26 00:20:40 christos Exp $");
52 1.1 christos
53 1.1 christos #include <sys/param.h>
54 1.1 christos #include <sys/mman.h>
55 1.1 christos #include <sys/mount.h>
56 1.1 christos #include <fcntl.h>
57 1.1 christos #include <unistd.h>
58 1.1 christos
59 1.1 christos #include <ffs/buf.h>
60 1.1 christos
61 1.1 christos #include <fs/msdosfs/bpb.h>
62 1.1 christos #include <fs/msdosfs/direntry.h>
63 1.1 christos #include <fs/msdosfs/denode.h>
64 1.1 christos #include <fs/msdosfs/msdosfsmount.h>
65 1.1 christos #include <fs/msdosfs/fat.h>
66 1.1 christos
67 1.1 christos #include "makefs.h"
68 1.1 christos #include "msdos.h"
69 1.1 christos
70 1.1 christos /*
71 1.1 christos * Some general notes:
72 1.1 christos *
73 1.1 christos * In the ufs filesystem the inodes, superblocks, and indirect blocks are
74 1.1 christos * read/written using the vnode for the filesystem. Blocks that represent
75 1.1 christos * the contents of a file are read/written using the vnode for the file
76 1.1 christos * (including directories when they are read/written as files). This
77 1.1 christos * presents problems for the dos filesystem because data that should be in
78 1.1 christos * an inode (if dos had them) resides in the directory itself. Since we
79 1.1 christos * must update directory entries without the benefit of having the vnode
80 1.1 christos * for the directory we must use the vnode for the filesystem. This means
81 1.1 christos * that when a directory is actually read/written (via read, write, or
82 1.1 christos * readdir, or seek) we must use the vnode for the filesystem instead of
83 1.1 christos * the vnode for the directory as would happen in ufs. This is to insure we
84 1.1 christos * retrieve the correct block from the buffer cache since the hash value is
85 1.1 christos * based upon the vnode address and the desired block number.
86 1.1 christos */
87 1.1 christos
88 1.1 christos static int msdosfs_wfile(const char *, struct denode *, fsnode *);
89 1.1 christos
90 1.1 christos static void
91 1.1 christos msdosfs_times(struct msdosfsmount *pmp, struct denode *dep,
92 1.1 christos const struct stat *st)
93 1.1 christos {
94 1.1 christos unix2dostime(&st->st_atimespec, pmp->pm_gmtoff, &dep->de_ADate,
95 1.1 christos NULL, NULL);
96 1.1 christos unix2dostime(&st->st_mtimespec, pmp->pm_gmtoff, &dep->de_MDate,
97 1.1 christos &dep->de_MTime, NULL);
98 1.1 christos }
99 1.1 christos
100 1.1 christos /*
101 1.1 christos * Create a regular file. On entry the directory to contain the file being
102 1.1 christos * created is locked. We must release before we return.
103 1.1 christos */
104 1.1 christos struct denode *
105 1.1 christos msdosfs_mkfile(const char *path, struct denode *pdep, fsnode *node)
106 1.1 christos {
107 1.1 christos struct componentname cn;
108 1.1 christos struct denode ndirent;
109 1.1 christos struct denode *dep;
110 1.1 christos int error;
111 1.1 christos struct stat *st = &node->inode->st;
112 1.1 christos struct msdosfsmount *pmp = pdep->de_pmp;
113 1.1 christos
114 1.1 christos cn.cn_nameptr = node->name;
115 1.1 christos cn.cn_namelen = strlen(node->name);
116 1.1 christos
117 1.1 christos #ifdef MSDOSFS_DEBUG
118 1.1 christos printf("msdosfs_create(cn %s, vap 0%o\n", node->name, st->st_mode);
119 1.1 christos #endif
120 1.1 christos
121 1.1 christos /*
122 1.1 christos * If this is the root directory and there is no space left we
123 1.1 christos * can't do anything. This is because the root directory can not
124 1.1 christos * change size.
125 1.1 christos */
126 1.1 christos if (pdep->de_StartCluster == MSDOSFSROOT
127 1.1 christos && pdep->de_fndoffset >= pdep->de_FileSize) {
128 1.1 christos error = ENOSPC;
129 1.1 christos goto bad;
130 1.1 christos }
131 1.1 christos
132 1.1 christos /*
133 1.1 christos * Create a directory entry for the file, then call createde() to
134 1.1 christos * have it installed. NOTE: DOS files are always executable. We
135 1.1 christos * use the absence of the owner write bit to make the file
136 1.1 christos * readonly.
137 1.1 christos */
138 1.1 christos memset(&ndirent, 0, sizeof(ndirent));
139 1.1 christos if ((error = uniqdosname(pdep, &cn, ndirent.de_Name)) != 0)
140 1.1 christos goto bad;
141 1.1 christos
142 1.1 christos ndirent.de_Attributes = (st->st_mode & S_IWUSR) ?
143 1.1 christos ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY;
144 1.1 christos ndirent.de_StartCluster = 0;
145 1.1 christos ndirent.de_FileSize = 0;
146 1.1 christos ndirent.de_dev = pdep->de_dev;
147 1.1 christos ndirent.de_devvp = pdep->de_devvp;
148 1.1 christos ndirent.de_pmp = pdep->de_pmp;
149 1.1 christos ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
150 1.1 christos msdosfs_times(pmp, &ndirent, st);
151 1.1 christos if ((error = createde(&ndirent, pdep, &dep, &cn)) != 0)
152 1.1 christos goto bad;
153 1.1 christos if ((error = msdosfs_wfile(path, dep, node)) == -1)
154 1.1 christos goto bad;
155 1.1 christos return dep;
156 1.1 christos
157 1.1 christos bad:
158 1.1 christos errno = error;
159 1.1 christos return NULL;
160 1.1 christos }
161 1.1 christos
162 1.1 christos /*
163 1.1 christos * Write data to a file or directory.
164 1.1 christos */
165 1.1 christos static int
166 1.1 christos msdosfs_wfile(const char *path, struct denode *dep, fsnode *node)
167 1.1 christos {
168 1.1 christos int error, fd;
169 1.1 christos size_t osize = dep->de_FileSize;
170 1.1 christos struct stat *st = &node->inode->st;
171 1.1 christos size_t nsize = st->st_size;
172 1.1 christos size_t offs = 0;
173 1.1 christos struct msdosfsmount *pmp = dep->de_pmp;
174 1.1 christos struct buf *bp;
175 1.1 christos char *dat;
176 1.1 christos
177 1.1 christos #ifdef MSDOSFS_DEBUG
178 1.1 christos printf("msdosfs_write(): diroff %lu, dirclust %lu, startcluster %lu\n",
179 1.1 christos dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster);
180 1.1 christos #endif
181 1.1 christos if (nsize == 0)
182 1.1 christos return 0;
183 1.1 christos
184 1.1 christos /* Don't bother to try to write files larger than the fs limit */
185 1.1 christos if (nsize > MSDOSFS_FILESIZE_MAX) {
186 1.1 christos errno = EFBIG;
187 1.1 christos return -1;
188 1.1 christos }
189 1.1 christos
190 1.1 christos if (nsize > osize) {
191 1.1 christos if ((error = deextend(dep, nsize, NULL)) != 0) {
192 1.1 christos errno = error;
193 1.1 christos return -1;
194 1.1 christos }
195 1.1 christos }
196 1.1 christos
197 1.1 christos if ((fd = open(path, O_RDONLY)) == -1)
198 1.1 christos err(1, "open %s", path);
199 1.1 christos
200 1.1 christos if ((dat = mmap(0, nsize, PROT_READ, MAP_FILE, fd, 0)) == MAP_FAILED)
201 1.1 christos err(1, "mmap %s", node->name);
202 1.1 christos close(fd);
203 1.1 christos
204 1.1 christos for (u_long cn = 0;; cn++) {
205 1.1 christos int blsize, cpsize;
206 1.1 christos daddr_t bn;
207 1.1 christos if (pcbmap(dep, cn, &bn, 0, &blsize)) {
208 1.1 christos fprintf(stderr, "pcbmap %ld\n", cn);
209 1.1 christos goto out;
210 1.1 christos }
211 1.1 christos
212 1.1 christos if (bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize, NULL,
213 1.1 christos 0, &bp)) {
214 1.1 christos fprintf(stderr, "bread\n");
215 1.1 christos }
216 1.1 christos cpsize = MIN((int)(nsize - offs), blsize);
217 1.1 christos memcpy(bp->b_data, dat + offs, cpsize);
218 1.1 christos bwrite(bp);
219 1.1 christos brelse(bp, 0);
220 1.1 christos offs += blsize;
221 1.1 christos if (offs >= nsize)
222 1.1 christos break;
223 1.1 christos }
224 1.1 christos
225 1.1 christos munmap(dat, nsize);
226 1.1 christos return 0;
227 1.1 christos out:
228 1.1 christos munmap(dat, nsize);
229 1.1 christos return -1;
230 1.1 christos }
231 1.1 christos
232 1.1 christos
233 1.1 christos static const struct {
234 1.1 christos struct direntry dot;
235 1.1 christos struct direntry dotdot;
236 1.1 christos } dosdirtemplate = {
237 1.1 christos { ". ", " ", /* the . entry */
238 1.1 christos ATTR_DIRECTORY, /* file attribute */
239 1.1 christos 0, /* reserved */
240 1.1 christos 0, { 0, 0 }, { 0, 0 }, /* create time & date */
241 1.1 christos { 0, 0 }, /* access date */
242 1.1 christos { 0, 0 }, /* high bits of start cluster */
243 1.1 christos { 210, 4 }, { 210, 4 }, /* modify time & date */
244 1.1 christos { 0, 0 }, /* startcluster */
245 1.1 christos { 0, 0, 0, 0 } /* filesize */
246 1.1 christos },
247 1.1 christos { ".. ", " ", /* the .. entry */
248 1.1 christos ATTR_DIRECTORY, /* file attribute */
249 1.1 christos 0, /* reserved */
250 1.1 christos 0, { 0, 0 }, { 0, 0 }, /* create time & date */
251 1.1 christos { 0, 0 }, /* access date */
252 1.1 christos { 0, 0 }, /* high bits of start cluster */
253 1.1 christos { 210, 4 }, { 210, 4 }, /* modify time & date */
254 1.1 christos { 0, 0 }, /* startcluster */
255 1.1 christos { 0, 0, 0, 0 } /* filesize */
256 1.1 christos }
257 1.1 christos };
258 1.1 christos
259 1.1 christos struct denode *
260 1.1 christos msdosfs_mkdire(const char *path, struct denode *pdep, fsnode *node) {
261 1.1 christos struct denode ndirent;
262 1.1 christos struct denode *dep;
263 1.1 christos struct componentname cn;
264 1.1 christos struct stat *st = &node->inode->st;
265 1.1 christos struct msdosfsmount *pmp = pdep->de_pmp;
266 1.1 christos int error;
267 1.1 christos u_long newcluster, pcl, bn;
268 1.1 christos daddr_t lbn;
269 1.1 christos struct direntry *denp;
270 1.1 christos struct buf *bp;
271 1.1 christos
272 1.1 christos cn.cn_nameptr = node->name;
273 1.1 christos cn.cn_namelen = strlen(node->name);
274 1.1 christos /*
275 1.1 christos * If this is the root directory and there is no space left we
276 1.1 christos * can't do anything. This is because the root directory can not
277 1.1 christos * change size.
278 1.1 christos */
279 1.1 christos if (pdep->de_StartCluster == MSDOSFSROOT
280 1.1 christos && pdep->de_fndoffset >= pdep->de_FileSize) {
281 1.1 christos error = ENOSPC;
282 1.1 christos goto bad2;
283 1.1 christos }
284 1.1 christos
285 1.1 christos /*
286 1.1 christos * Allocate a cluster to hold the about to be created directory.
287 1.1 christos */
288 1.1 christos error = clusteralloc(pmp, 0, 1, &newcluster, NULL);
289 1.1 christos if (error)
290 1.1 christos goto bad2;
291 1.1 christos
292 1.1 christos memset(&ndirent, 0, sizeof(ndirent));
293 1.1 christos ndirent.de_pmp = pmp;
294 1.1 christos ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
295 1.1 christos msdosfs_times(pmp, &ndirent, st);
296 1.1 christos
297 1.1 christos /*
298 1.1 christos * Now fill the cluster with the "." and ".." entries. And write
299 1.1 christos * the cluster to disk. This way it is there for the parent
300 1.1 christos * directory to be pointing at if there were a crash.
301 1.1 christos */
302 1.1 christos bn = cntobn(pmp, newcluster);
303 1.1 christos lbn = de_bn2kb(pmp, bn);
304 1.1 christos /* always succeeds */
305 1.1 christos bp = getblk(pmp->pm_devvp, lbn, pmp->pm_bpcluster, 0, 0);
306 1.1 christos memset(bp->b_data, 0, pmp->pm_bpcluster);
307 1.1 christos memcpy(bp->b_data, &dosdirtemplate, sizeof dosdirtemplate);
308 1.1 christos denp = (struct direntry *)bp->b_data;
309 1.1 christos putushort(denp[0].deStartCluster, newcluster);
310 1.1 christos putushort(denp[0].deCDate, ndirent.de_CDate);
311 1.1 christos putushort(denp[0].deCTime, ndirent.de_CTime);
312 1.1 christos denp[0].deCHundredth = ndirent.de_CHun;
313 1.1 christos putushort(denp[0].deADate, ndirent.de_ADate);
314 1.1 christos putushort(denp[0].deMDate, ndirent.de_MDate);
315 1.1 christos putushort(denp[0].deMTime, ndirent.de_MTime);
316 1.1 christos pcl = pdep->de_StartCluster;
317 1.1 christos if (FAT32(pmp) && pcl == pmp->pm_rootdirblk)
318 1.1 christos pcl = 0;
319 1.1 christos putushort(denp[1].deStartCluster, pcl);
320 1.1 christos putushort(denp[1].deCDate, ndirent.de_CDate);
321 1.1 christos putushort(denp[1].deCTime, ndirent.de_CTime);
322 1.1 christos denp[1].deCHundredth = ndirent.de_CHun;
323 1.1 christos putushort(denp[1].deADate, ndirent.de_ADate);
324 1.1 christos putushort(denp[1].deMDate, ndirent.de_MDate);
325 1.1 christos putushort(denp[1].deMTime, ndirent.de_MTime);
326 1.1 christos if (FAT32(pmp)) {
327 1.1 christos putushort(denp[0].deHighClust, newcluster >> 16);
328 1.1 christos putushort(denp[1].deHighClust, pdep->de_StartCluster >> 16);
329 1.1 christos } else {
330 1.1 christos putushort(denp[0].deHighClust, 0);
331 1.1 christos putushort(denp[1].deHighClust, 0);
332 1.1 christos }
333 1.1 christos
334 1.1 christos if ((error = bwrite(bp)) != 0)
335 1.1 christos goto bad;
336 1.1 christos
337 1.1 christos /*
338 1.1 christos * Now build up a directory entry pointing to the newly allocated
339 1.1 christos * cluster. This will be written to an empty slot in the parent
340 1.1 christos * directory.
341 1.1 christos */
342 1.1 christos if ((error = uniqdosname(pdep, &cn, ndirent.de_Name)) != 0)
343 1.1 christos goto bad;
344 1.1 christos
345 1.1 christos ndirent.de_Attributes = ATTR_DIRECTORY;
346 1.1 christos ndirent.de_StartCluster = newcluster;
347 1.1 christos ndirent.de_FileSize = 0;
348 1.1 christos ndirent.de_dev = pdep->de_dev;
349 1.1 christos ndirent.de_devvp = pdep->de_devvp;
350 1.1 christos if ((error = createde(&ndirent, pdep, &dep, &cn)) != 0)
351 1.1 christos goto bad;
352 1.1 christos return dep;
353 1.1 christos
354 1.1 christos bad:
355 1.1 christos clusterfree(pmp, newcluster, NULL);
356 1.1 christos bad2:
357 1.1 christos errno = error;
358 1.1 christos return NULL;
359 1.1 christos }
360