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