Commit | Line | Data |
---|---|---|
ad59feb8 RG |
1 | /* |
2 | * Written by Paul Popelka (paulp@uts.amdahl.com) | |
3 | * | |
4 | * You can do anything you want with this software, just don't say you wrote | |
5 | * it, and don't remove this notice. | |
6 | * | |
7 | * This software is provided "as is". | |
8 | * | |
9 | * The author supplies this software to be publicly redistributed on the | |
10 | * understanding that the author is not responsible for the correct | |
11 | * functioning of this software in any circumstances and is not liable for | |
12 | * any damages caused by this software. | |
13 | * | |
14 | * October 1992 | |
15 | * | |
16 | * from NetBSD: msdosfs_vnops.c,v 1.1 1993/08/13 11:35:40 cgd Exp | |
d79b78e0 | 17 | * $Id: msdosfs_vnops.c,v 1.1 1994/01/24 06:04:57 rgrimes Exp $ |
ad59feb8 RG |
18 | */ |
19 | ||
20 | #include <sys/param.h> | |
21 | #include <sys/systm.h> | |
22 | #include <sys/namei.h> | |
23 | #include <sys/resourcevar.h> /* defines plimit structure in proc struct */ | |
24 | #include <sys/kernel.h> | |
25 | #include <sys/file.h> /* define FWRITE ... */ | |
26 | #include <sys/stat.h> | |
27 | #include <sys/buf.h> | |
28 | #include <sys/proc.h> | |
29 | #include <sys/mount.h> | |
30 | #include <sys/vnode.h> | |
d79b78e0 | 31 | #include <fs/specfs/specdev.h> /* XXX */ /* defines v_rdev */ |
ad59feb8 RG |
32 | #include <sys/malloc.h> |
33 | #include <sys/dir.h> /* defines dirent structure */ | |
34 | ||
d79b78e0 RG |
35 | #include <fs/msdosfs/bpb.h> |
36 | #include <fs/msdosfs/direntry.h> | |
37 | #include <fs/msdosfs/denode.h> | |
38 | #include <fs/msdosfs/msdosfsmount.h> | |
39 | #include <fs/msdosfs/fat.h> | |
ad59feb8 RG |
40 | /* |
41 | * Some general notes: | |
42 | * | |
43 | * In the ufs filesystem the inodes, superblocks, and indirect blocks are | |
44 | * read/written using the vnode for the filesystem. Blocks that represent | |
45 | * the contents of a file are read/written using the vnode for the file | |
46 | * (including directories when they are read/written as files). This | |
47 | * presents problems for the dos filesystem because data that should be in | |
48 | * an inode (if dos had them) resides in the directory itself. Since we | |
49 | * must update directory entries without the benefit of having the vnode | |
50 | * for the directory we must use the vnode for the filesystem. This means | |
51 | * that when a directory is actually read/written (via read, write, or | |
52 | * readdir, or seek) we must use the vnode for the filesystem instead of | |
53 | * the vnode for the directory as would happen in ufs. This is to insure we | |
54 | * retreive the correct block from the buffer cache since the hash value is | |
55 | * based upon the vnode address and the desired block number. | |
56 | */ | |
57 | ||
58 | /* | |
59 | * Create a regular file. On entry the directory to contain the file being | |
60 | * created is locked. We must release before we return. We must also free | |
61 | * the pathname buffer pointed at by ndp->ni_pnbuf, always on error, or | |
62 | * only if the SAVESTART bit in ni_nameiop is clear on success. | |
63 | */ | |
64 | int | |
65 | msdosfs_create(ndp, vap, p) | |
66 | struct nameidata *ndp; | |
67 | struct vattr *vap; | |
68 | struct proc *p; | |
69 | { | |
70 | struct denode ndirent; | |
71 | struct direntry *ndirp = &ndirent.de_de; | |
72 | struct denode *dep; | |
73 | struct denode *pdep = VTODE(ndp->ni_dvp); | |
74 | int error; | |
75 | ||
76 | #if defined(MSDOSFSDEBUG) | |
77 | printf("msdosfs_create(ndp %08x, vap %08x, p %08x\n", ndp, vap, p); | |
78 | #endif /* defined(MSDOSFSDEBUG) */ | |
79 | ||
80 | /* | |
81 | * Create a directory entry for the file, then call createde() to | |
82 | * have it installed. NOTE: DOS files are always executable. We | |
83 | * use the absence of the owner write bit to make the file | |
84 | * readonly. | |
85 | */ | |
86 | bzero(&ndirent, sizeof(ndirent)); | |
87 | unix2dostime(&time, (union dosdate *) & ndirp->deDate, | |
88 | (union dostime *) & ndirp->deTime); | |
89 | unix2dosfn((u_char *) ndp->ni_ptr, ndirp->deName, ndp->ni_namelen); | |
90 | ndirp->deAttributes = (vap->va_mode & VWRITE) ? 0 : ATTR_READONLY; | |
91 | ndirp->deStartCluster = 0; | |
92 | ndirp->deFileSize = 0; | |
93 | ndirent.de_pmp = pdep->de_pmp; | |
94 | ndirent.de_dev = pdep->de_dev; | |
95 | ndirent.de_devvp = pdep->de_devvp; | |
96 | if ((error = createde(&ndirent, ndp, &dep)) == 0) { | |
97 | ndp->ni_vp = DETOV(dep); | |
98 | if ((ndp->ni_nameiop & SAVESTART) == 0) | |
99 | free(ndp->ni_pnbuf, M_NAMEI); | |
100 | } | |
101 | else { | |
102 | free(ndp->ni_pnbuf, M_NAMEI); | |
103 | } | |
104 | deput(pdep); /* release parent dir */ | |
105 | return error; | |
106 | } | |
107 | ||
108 | int | |
109 | msdosfs_mknod(ndp, vap, cred, p) | |
110 | struct nameidata *ndp; | |
111 | struct vattr *vap; | |
112 | struct ucred *cred; | |
113 | struct proc *p; | |
114 | { | |
115 | int error; | |
116 | struct denode *pdep = VTODE(ndp->ni_dvp); | |
117 | ||
118 | switch (vap->va_type) { | |
119 | case VDIR: | |
120 | error = msdosfs_mkdir(ndp, vap, p); | |
121 | break; | |
122 | ||
123 | /* | |
124 | * msdosfs_create() sets ndp->ni_vp. | |
125 | */ | |
126 | case VREG: | |
127 | error = msdosfs_create(ndp, vap, p); | |
128 | break; | |
129 | ||
130 | default: | |
131 | error = EINVAL; | |
132 | free(ndp->ni_pnbuf, M_NAMEI); | |
133 | deput(pdep); | |
134 | break; | |
135 | } | |
136 | return error; | |
137 | } | |
138 | ||
139 | int | |
140 | msdosfs_open(vp, mode, cred, p) | |
141 | struct vnode *vp; | |
142 | int mode; | |
143 | struct ucred *cred; | |
144 | struct proc *p; | |
145 | { | |
146 | return 0; | |
147 | } | |
148 | ||
149 | int | |
150 | msdosfs_close(vp, fflag, cred, p) | |
151 | struct vnode *vp; | |
152 | int fflag; | |
153 | struct ucred *cred; | |
154 | struct proc *p; | |
155 | { | |
156 | struct denode *dep = VTODE(vp); | |
157 | ||
158 | if (vp->v_usecount > 1 && !(dep->de_flag & DELOCKED)) | |
159 | DETIMES(dep, &time); | |
160 | return 0; | |
161 | } | |
162 | ||
163 | int | |
164 | msdosfs_access(vp, mode, cred, p) | |
165 | struct vnode *vp; | |
166 | int mode; | |
167 | struct ucred *cred; | |
168 | struct proc *p; | |
169 | { | |
170 | int dosmode; | |
171 | struct denode *dep = VTODE(vp); | |
172 | ||
173 | /* | |
174 | * Root gets to do anything. Even execute a file without the x-bit | |
175 | * on? But, for dos filesystems every file is executable. I may | |
176 | * regret this. | |
177 | */ | |
178 | if (cred->cr_uid == 0) | |
179 | return 0; | |
180 | ||
181 | /* | |
182 | * mode is filled with a combination of VREAD, VWRITE, and/or VEXEC | |
183 | * bits turned on. In an octal number these are the Y in 0Y00. | |
184 | * | |
185 | * Since the dos filesystem doesn't have the concept of file ownership | |
186 | * we just give everybody read and execute access and write access | |
187 | * if the readonly bit is off. | |
188 | */ | |
189 | dosmode = VEXEC | VREAD | | |
190 | ((dep->de_Attributes & ATTR_READONLY) ? 0 : VWRITE); | |
191 | return ((dosmode & mode) != 0) ? 0 : EACCES; | |
192 | } | |
193 | ||
194 | int | |
195 | msdosfs_getattr(vp, vap, cred, p) | |
196 | struct vnode *vp; | |
197 | struct vattr *vap; | |
198 | struct ucred *cred; | |
199 | struct proc *p; | |
200 | { | |
201 | u_int cn; | |
202 | struct denode *dep = VTODE(vp); | |
203 | ||
204 | DETIMES(dep, &time); | |
205 | vap->va_fsid = dep->de_dev; | |
206 | /* | |
207 | * The following computation of the fileid must be the same as that | |
208 | * used in msdosfs_readdir() to compute d_fileno. If not, pwd | |
209 | * doesn't work. | |
210 | */ | |
211 | if (dep->de_Attributes & ATTR_DIRECTORY) { | |
212 | if ((cn = dep->de_StartCluster) == MSDOSFSROOT) | |
213 | cn = 1; | |
214 | } | |
215 | else { | |
216 | if ((cn = dep->de_dirclust) == MSDOSFSROOT) | |
217 | cn = 1; | |
218 | cn = (cn << 16) | (dep->de_diroffset & 0xffff); | |
219 | } | |
220 | vap->va_fileid = cn; | |
221 | vap->va_mode = (dep->de_Attributes & ATTR_READONLY) ? 0555 : 0777; | |
222 | if (dep->de_Attributes & ATTR_DIRECTORY) | |
223 | vap->va_mode |= S_IFDIR; | |
224 | vap->va_nlink = 1; | |
225 | vap->va_gid = 0; | |
226 | vap->va_uid = 0; | |
227 | vap->va_rdev = 0; | |
228 | vap->va_size = dep->de_FileSize; | |
229 | vap->va_size_rsv = 0; | |
230 | dos2unixtime((union dosdate *) & dep->de_Date, | |
231 | (union dostime *) & dep->de_Time, &vap->va_atime); | |
232 | vap->va_atime.tv_usec = 0; | |
233 | vap->va_mtime.tv_sec = vap->va_atime.tv_sec; | |
234 | vap->va_mtime.tv_usec = 0; | |
235 | #ifndef MSDOSFS_NODIRMOD | |
236 | if (vap->va_mode & S_IFDIR) { | |
237 | vap->va_mtime.tv_sec = time.tv_sec; | |
238 | vap->va_mtime.tv_usec = time.tv_usec; | |
239 | } | |
240 | #endif | |
241 | vap->va_ctime.tv_sec = vap->va_atime.tv_sec; | |
242 | vap->va_ctime.tv_usec = 0; | |
243 | vap->va_flags = dep->de_flag; | |
244 | vap->va_gen = 0; | |
245 | vap->va_blocksize = dep->de_pmp->pm_bpcluster; | |
246 | vap->va_bytes = (dep->de_FileSize + dep->de_pmp->pm_crbomask) & | |
247 | ~(dep->de_pmp->pm_crbomask); | |
248 | vap->va_bytes_rsv = 0; | |
249 | vap->va_type = vp->v_type; | |
250 | return 0; | |
251 | } | |
252 | ||
253 | int | |
254 | msdosfs_setattr(vp, vap, cred, p) | |
255 | struct vnode *vp; | |
256 | struct vattr *vap; | |
257 | struct ucred *cred; | |
258 | struct proc *p; | |
259 | { | |
260 | int error = 0; | |
261 | struct denode *dep = VTODE(vp); | |
262 | ||
263 | #if defined(MSDOSFSDEBUG) | |
264 | printf("msdosfs_setattr(): vp %08x, vap %08x, cred %08x, p %08x\n", | |
265 | vp, vap, cred, p); | |
266 | #endif /* defined(MSDOSFSDEBUG) */ | |
267 | if ((vap->va_type != VNON) || | |
268 | (vap->va_nlink != VNOVAL) || | |
269 | (vap->va_fsid != VNOVAL) || | |
270 | (vap->va_fileid != VNOVAL) || | |
271 | (vap->va_blocksize != VNOVAL) || | |
272 | (vap->va_rdev != VNOVAL) || | |
273 | (vap->va_bytes != VNOVAL) || | |
274 | (vap->va_gen != VNOVAL) || | |
275 | (vap->va_uid != (u_short) VNOVAL) || | |
276 | (vap->va_gid != (u_short) VNOVAL) || | |
277 | (vap->va_atime.tv_sec != VNOVAL)) { | |
278 | #if defined(MSDOSFSDEBUG) | |
279 | printf("msdosfs_setattr(): returning EINVAL\n"); | |
280 | printf(" va_type %d, va_nlink %x, va_fsid %x, va_fileid %x\n", | |
281 | vap->va_type, vap->va_nlink, vap->va_fsid, vap->va_fileid); | |
282 | printf(" va_blocksize %x, va_rdev %x, va_bytes %x, va_gen %x\n", | |
283 | vap->va_blocksize, vap->va_rdev, vap->va_bytes, vap->va_gen); | |
284 | printf(" va_uid %x, va_gid %x, va_atime.tv_sec %x\n", | |
285 | vap->va_uid, vap->va_gid, vap->va_atime.tv_sec); | |
286 | #endif /* defined(MSDOSFSDEBUG) */ | |
287 | return EINVAL; | |
288 | } | |
289 | ||
290 | if (vap->va_size != VNOVAL) { | |
291 | if (vp->v_type == VDIR) | |
292 | return EISDIR; | |
293 | if (error = detrunc(dep, vap->va_size, 0)) | |
294 | return error; | |
295 | } | |
296 | if (vap->va_mtime.tv_sec != VNOVAL) { | |
297 | dep->de_flag |= DEUPD; | |
298 | if (error = deupdat(dep, &vap->va_mtime, 1)) | |
299 | return error; | |
300 | } | |
301 | ||
302 | /* | |
303 | * DOS files only have the ability to have thier writability | |
304 | * attribute set, so we use the owner write bit to set the readonly | |
305 | * attribute. | |
306 | */ | |
307 | if (vap->va_mode != (u_short) VNOVAL) { | |
308 | /* We ignore the read and execute bits */ | |
309 | if (vap->va_mode & VWRITE) | |
310 | dep->de_Attributes &= ~ATTR_READONLY; | |
311 | else | |
312 | dep->de_Attributes |= ATTR_READONLY; | |
313 | dep->de_flag |= DEUPD; | |
314 | } | |
315 | ||
316 | if (vap->va_flags != VNOVAL) { | |
317 | if (error = suser(cred, &p->p_acflag)) | |
318 | return error; | |
319 | if (cred->cr_uid == 0) | |
320 | dep->de_flag = vap->va_flags; | |
321 | else { | |
322 | dep->de_flag &= 0xffff0000; | |
323 | dep->de_flag |= (vap->va_flags & 0xffff); | |
324 | } | |
325 | dep->de_flag |= DEUPD; | |
326 | } | |
327 | return error; | |
328 | } | |
329 | ||
330 | ||
331 | int | |
332 | msdosfs_read(vp, uio, ioflag, cred) | |
333 | struct vnode *vp; | |
334 | struct uio *uio; | |
335 | int ioflag; | |
336 | struct ucred *cred; | |
337 | { | |
338 | int error = 0; | |
339 | int diff; | |
340 | int isadir; | |
341 | long n; | |
342 | long on; | |
343 | daddr_t bn; | |
344 | daddr_t lbn; | |
345 | daddr_t rablock; | |
346 | struct buf *bp; | |
347 | struct denode *dep = VTODE(vp); | |
348 | struct msdosfsmount *pmp = dep->de_pmp; | |
349 | ||
350 | /* | |
351 | * If they didn't ask for any data, then we are done. | |
352 | */ | |
353 | if (uio->uio_resid == 0) | |
354 | return 0; | |
355 | if (uio->uio_offset < 0) | |
356 | return EINVAL; | |
357 | ||
358 | isadir = dep->de_Attributes & ATTR_DIRECTORY; | |
359 | do { | |
360 | lbn = uio->uio_offset >> pmp->pm_cnshift; | |
361 | on = uio->uio_offset & pmp->pm_crbomask; | |
362 | n = MIN((u_long) (pmp->pm_bpcluster - on), uio->uio_resid); | |
363 | diff = dep->de_FileSize - uio->uio_offset; | |
364 | if (diff <= 0) | |
365 | return 0; | |
366 | /* convert cluster # to block # if a directory */ | |
367 | if (isadir) { | |
368 | error = pcbmap(dep, lbn, &lbn, 0); | |
369 | if (error) | |
370 | return error; | |
371 | } | |
372 | if (diff < n) | |
373 | n = diff; | |
374 | /* | |
375 | * If we are operating on a directory file then be sure to | |
376 | * do i/o with the vnode for the filesystem instead of the | |
377 | * vnode for the directory. | |
378 | */ | |
379 | if (isadir) { | |
380 | error = bread(pmp->pm_devvp, lbn, pmp->pm_bpcluster, | |
381 | NOCRED, &bp); | |
382 | } | |
383 | else { | |
384 | rablock = lbn + 1; | |
385 | if (vp->v_lastr + 1 == lbn && | |
386 | rablock * pmp->pm_bpcluster < dep->de_FileSize) { | |
387 | error = breada(vp, lbn, pmp->pm_bpcluster, | |
388 | rablock, pmp->pm_bpcluster, NOCRED, &bp); | |
389 | } | |
390 | else { | |
391 | error = bread(vp, lbn, pmp->pm_bpcluster, NOCRED, | |
392 | &bp); | |
393 | } | |
394 | vp->v_lastr = lbn; | |
395 | } | |
396 | n = MIN(n, pmp->pm_bpcluster - bp->b_resid); | |
397 | if (error) { | |
398 | brelse(bp); | |
399 | return error; | |
400 | } | |
401 | error = uiomove(bp->b_un.b_addr + on, (int) n, uio); | |
402 | /* | |
403 | * If we have read everything from this block or have read | |
404 | * to end of file then we are done with this block. Mark | |
405 | * it to say the buffer can be reused if need be. | |
406 | */ | |
407 | #if 0 | |
408 | if (n + on == pmp->pm_bpcluster || | |
409 | uio->uio_offset == dep->de_FileSize) | |
410 | bp->b_flags |= B_AGE; | |
411 | #endif | |
412 | brelse(bp); | |
413 | } while (error == 0 && uio->uio_resid > 0 && n != 0); | |
414 | return error; | |
415 | } | |
416 | ||
417 | /* | |
418 | * Write data to a file or directory. | |
419 | */ | |
420 | int | |
421 | msdosfs_write(vp, uio, ioflag, cred) | |
422 | struct vnode *vp; | |
423 | struct uio *uio; | |
424 | int ioflag; | |
425 | struct ucred *cred; | |
426 | { | |
427 | int n; | |
428 | int isadir; | |
429 | int croffset; | |
430 | int resid; | |
431 | int osize; | |
432 | int error; | |
433 | u_long cluster; | |
434 | u_long nextcluster; | |
435 | u_long lastcluster; | |
436 | daddr_t bn; | |
437 | struct buf *bp; | |
438 | struct proc *p = uio->uio_procp; | |
439 | struct vnode *thisvp; | |
440 | struct denode *dep = VTODE(vp); | |
441 | struct msdosfsmount *pmp = dep->de_pmp; | |
442 | ||
443 | #if defined(MSDOSFSDEBUG) | |
444 | printf("msdosfs_write(vp %08x, uio %08x, ioflag %08x, cred %08x\n", | |
445 | vp, uio, ioflag, cred); | |
446 | printf("msdosfs_write(): diroff %d, dirclust %d, startcluster %d\n", | |
447 | dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster); | |
448 | #endif /* defined(MSDOSFSDEBUG) */ | |
449 | ||
450 | switch (vp->v_type) { | |
451 | case VREG: | |
452 | if (ioflag & IO_APPEND) | |
453 | uio->uio_offset = dep->de_FileSize; | |
454 | isadir = 0; | |
455 | thisvp = vp; | |
456 | break; | |
457 | ||
458 | case VDIR: | |
459 | if ((ioflag & IO_SYNC) == 0) | |
460 | panic("msdosfs_write(): non-sync directory update"); | |
461 | isadir = 1; | |
462 | thisvp = pmp->pm_devvp; | |
463 | break; | |
464 | ||
465 | default: | |
466 | panic("msdosfs_write(): bad file type"); | |
467 | break; | |
468 | } | |
469 | ||
470 | if (uio->uio_offset < 0) { | |
471 | return EINVAL; | |
472 | } | |
473 | if (uio->uio_resid == 0) | |
474 | return 0; | |
475 | ||
476 | /* | |
477 | * If they've exceeded their filesize limit, tell them about it. | |
478 | */ | |
479 | if (vp->v_type == VREG && p && | |
480 | ((uio->uio_offset + uio->uio_resid) > | |
481 | p->p_rlimit[RLIMIT_FSIZE].rlim_cur)) { | |
482 | psignal(p, SIGXFSZ); | |
483 | return EFBIG; | |
484 | } | |
485 | ||
486 | /* | |
487 | * If attempting to write beyond the end of the root directory we | |
488 | * stop that here because the root directory can not grow. | |
489 | */ | |
490 | if ((dep->de_Attributes & ATTR_DIRECTORY) && | |
491 | dep->de_StartCluster == MSDOSFSROOT && | |
492 | (uio->uio_offset + uio->uio_resid) > dep->de_FileSize) | |
493 | return ENOSPC; | |
494 | ||
495 | /* | |
496 | * If the offset we are starting the write at is beyond the end of | |
497 | * the file, then they've done a seek. Unix filesystems allow | |
498 | * files with holes in them, DOS doesn't so we must fill the hole | |
499 | * with zeroed blocks. We do this by calling our seek function. | |
500 | * This could probably be cleaned up someday. | |
501 | */ | |
502 | if (uio->uio_offset > dep->de_FileSize) { | |
503 | error = msdosfs_seek(vp, (off_t) 0, uio->uio_offset, cred); | |
504 | if (error) | |
505 | return error; | |
506 | } | |
507 | ||
508 | /* | |
509 | * Remember some values in case the write fails. | |
510 | */ | |
511 | resid = uio->uio_resid; | |
512 | osize = dep->de_FileSize; | |
513 | ||
514 | do { | |
515 | bn = uio->uio_offset >> pmp->pm_cnshift; | |
516 | /* | |
517 | * If we are appending to the file and we are on a cluster | |
518 | * boundary, then allocate a new cluster and chain it onto | |
519 | * the file. | |
520 | */ | |
521 | if (uio->uio_offset == dep->de_FileSize && | |
522 | (uio->uio_offset & pmp->pm_crbomask) == 0) { | |
523 | if (error = extendfile(dep, &bp, 0)) | |
524 | break; | |
525 | } | |
526 | else { | |
527 | /* | |
528 | * The block we need to write into exists, so just | |
529 | * read it in. | |
530 | */ | |
531 | if (isadir) { | |
532 | error = pcbmap(dep, bn, &bn, 0); | |
533 | if (error) | |
534 | return error; | |
535 | } | |
536 | error = bread(thisvp, bn, pmp->pm_bpcluster, cred, &bp); | |
537 | if (error) | |
538 | return error; | |
539 | } | |
540 | croffset = uio->uio_offset & pmp->pm_crbomask; | |
541 | n = MIN(uio->uio_resid, pmp->pm_bpcluster - croffset); | |
542 | if (uio->uio_offset + n > dep->de_FileSize) { | |
543 | dep->de_FileSize = uio->uio_offset + n; | |
544 | vnode_pager_setsize(vp, dep->de_FileSize); /* why? */ | |
545 | } | |
546 | (void) vnode_pager_uncache(vp); /* why not? */ | |
547 | /* | |
548 | * Should these vnode_pager_* functions be done on dir | |
549 | * files? | |
550 | */ | |
551 | ||
552 | /* | |
553 | * Copy the data from user space into the buf header. | |
554 | */ | |
555 | error = uiomove(bp->b_un.b_addr + croffset, n, uio); | |
556 | ||
557 | /* | |
558 | * If they want this synchronous then write it and wait for | |
559 | * it. Otherwise, if on a cluster boundary write it | |
560 | * asynchronously so we can move on to the next block | |
561 | * without delay. Otherwise do a delayed write because we | |
562 | * may want to write somemore into the block later. | |
563 | */ | |
564 | if (ioflag & IO_SYNC) | |
565 | (void) bwrite(bp); | |
566 | else if (n + croffset == pmp->pm_bpcluster) { | |
567 | bp->b_flags |= B_AGE; | |
568 | bawrite(bp); | |
569 | } | |
570 | else | |
571 | bdwrite(bp); | |
572 | dep->de_flag |= DEUPD; | |
573 | } while (error == 0 && uio->uio_resid > 0); | |
574 | ||
575 | /* | |
576 | * If the write failed and they want us to, truncate the file back | |
577 | * to the size it was before the write was attempted. | |
578 | */ | |
579 | if (error && (ioflag & IO_UNIT)) { | |
580 | detrunc(dep, osize, ioflag & IO_SYNC); | |
581 | uio->uio_offset -= resid - uio->uio_resid; | |
582 | uio->uio_resid = resid; | |
583 | } | |
584 | if (!error && (ioflag & IO_UNIT)) | |
585 | error = deupdat(dep, &time, 1); | |
586 | return error; | |
587 | } | |
588 | ||
589 | int | |
590 | msdosfs_ioctl(vp, com, data, fflag, cred, p) | |
591 | struct vnode *vp; | |
592 | int com; | |
593 | caddr_t data; | |
594 | struct ucred *cred; | |
595 | struct proc *p; | |
596 | { | |
597 | return ENOTTY; | |
598 | } | |
599 | ||
600 | int | |
601 | msdosfs_select(vp, which, fflags, cred, p) | |
602 | struct vnode *vp; | |
603 | int which; | |
604 | int fflags; | |
605 | struct ucred *cred; | |
606 | struct proc *p; | |
607 | { | |
608 | return 1; /* DOS filesystems never block? */ | |
609 | } | |
610 | ||
611 | int | |
612 | msdosfs_mmap(vp, fflags, cred, p) | |
613 | struct vnode *vp; | |
614 | int fflags; | |
615 | struct ucred *cred; | |
616 | struct proc *p; | |
617 | { | |
618 | return EINVAL; | |
619 | } | |
620 | ||
621 | /* | |
622 | * Flush the blocks of a file to disk. | |
623 | * | |
624 | * This function is worthless for vnodes that represent directories. Maybe we | |
625 | * could just do a sync if they try an fsync on a directory file. | |
626 | */ | |
627 | int | |
628 | msdosfs_fsync(vp, fflags, cred, waitfor, p) | |
629 | struct vnode *vp; | |
630 | int fflags; | |
631 | struct ucred *cred; | |
632 | int waitfor; | |
633 | struct proc *p; | |
634 | { | |
635 | struct denode *dep = VTODE(vp); | |
636 | ||
637 | if (fflags & FWRITE) | |
638 | dep->de_flag |= DEUPD; | |
639 | /* | |
640 | * Does this call to vflushbuf() do anything? I can find no code | |
641 | * anywhere that sets v_dirtyblkhd in the vnode, which vflushbuf() | |
642 | * seems to depend upon. | |
643 | */ | |
644 | vflushbuf(vp, waitfor == MNT_WAIT ? B_SYNC : 0); | |
645 | return deupdat(dep, &time, waitfor == MNT_WAIT); | |
646 | } | |
647 | ||
648 | /* | |
649 | * Since the dos filesystem does not allow files with holes in them we must | |
650 | * fill the file with zeroed blocks when a seek past the end of file | |
651 | * happens. | |
652 | * | |
653 | * It seems that nothing in the kernel calls the filesystem specific file seek | |
654 | * functions. And, someone on the net told me that NFS never sends | |
655 | * announcements of seeks to the server. So, if msdosfs ever becomes NFS | |
656 | * mountable it will have to use other means to fill in holes in what would | |
657 | * be a sparse file. (This appears fixed since msdosfs_write() calls seek | |
658 | * before writing if the offset is past EOF) | |
659 | */ | |
660 | int | |
661 | msdosfs_seek(vp, oldoff, newoff, cred) | |
662 | struct vnode *vp; | |
663 | off_t oldoff; | |
664 | off_t newoff; | |
665 | struct ucred *cred; | |
666 | { | |
667 | int error = 0; | |
668 | off_t foff; | |
669 | daddr_t bn; | |
670 | u_long cluster; | |
671 | u_long lastcluster; | |
672 | struct buf *bp; | |
673 | struct denode *dep = VTODE(vp); | |
674 | struct msdosfsmount *pmp = dep->de_pmp; | |
675 | ||
676 | #if defined(MSDOSFSDEBUG) | |
677 | printf("msdosfs_seek(vp %08x, oldoff %d, newoff %d, cred %08x)\n", | |
678 | vp, oldoff, newoff, cred); | |
679 | #endif /* defined(MSDOSFSDEBUG) */ | |
680 | ||
681 | /* | |
682 | * Compute the offset of the first byte after the last block in the | |
683 | * file. If seeking beyond the end of file then fill the file with | |
684 | * zeroed blocks up to the seek address. | |
685 | */ | |
686 | foff = (dep->de_FileSize + (pmp->pm_bpcluster - 1)) & ~pmp->pm_crbomask; | |
687 | #if defined(MSDOSFSDEBUG) | |
688 | printf("seek: newoff %d > foff %d\n", newoff, foff); | |
689 | #endif /* defined(MSDOSFSDEBUG) */ | |
690 | if (newoff > foff) { | |
691 | /* | |
692 | * If this is the root directory and we are attempting to | |
693 | * seek beyond the end disallow it. DOS filesystem root | |
694 | * directories can not grow. | |
695 | */ | |
696 | if (vp->v_flag & VROOT) | |
697 | return EINVAL; | |
698 | /* | |
699 | * If this is a directory and the caller is not root, then | |
700 | * do not let them seek beyond the end of file. If we | |
701 | * allowed this then users could cause directories to grow. | |
702 | * Is this really that important? | |
703 | */ | |
704 | if (dep->de_Attributes & ATTR_DIRECTORY) { | |
705 | if (error = suser(cred, NULL)) { | |
706 | return error; | |
707 | } | |
708 | } | |
709 | /* | |
710 | * Allocate and chain together as many clusters as are | |
711 | * needed to get to newoff. | |
712 | */ | |
713 | while (foff < newoff) { | |
714 | if (error = extendfile(dep, &bp, 0)) | |
715 | return error; | |
716 | dep->de_flag |= DEUPD; | |
717 | bdwrite(bp); | |
718 | foff += pmp->pm_bpcluster; | |
719 | dep->de_FileSize += pmp->pm_bpcluster; | |
720 | } /* end while() */ | |
721 | dep->de_FileSize = newoff; | |
722 | return deupdat(dep, &time); | |
723 | } | |
724 | return 0; | |
725 | } | |
726 | ||
727 | int | |
728 | msdosfs_remove(ndp, p) | |
729 | struct nameidata *ndp; | |
730 | struct proc *p; | |
731 | { | |
732 | int error; | |
733 | struct denode *dep = VTODE(ndp->ni_vp); | |
734 | struct denode *ddep = VTODE(ndp->ni_dvp); | |
735 | ||
736 | error = removede(ndp); | |
737 | #if defined(MSDOSFSDEBUG) | |
738 | printf("msdosfs_remove(), dep %08x, v_usecount %d\n", dep, ndp->ni_vp->v_usecount); | |
739 | #endif /* defined(MSDOSFSDEBUG) */ | |
740 | if (ddep == dep) | |
741 | vrele(DETOV(dep)); | |
742 | else | |
743 | deput(dep); /* causes msdosfs_inactive() to be called | |
744 | * via vrele() */ | |
745 | deput(ddep); | |
746 | return error; | |
747 | } | |
748 | ||
749 | /* | |
750 | * DOS filesystems don't know what links are. But since we already called | |
751 | * msdosfs_lookup() with create and lockparent, the parent is locked so we | |
752 | * have to free it before we return the error. | |
753 | */ | |
754 | int | |
755 | msdosfs_link(vp, ndp, p) | |
756 | struct vnode *vp; | |
757 | struct nameidata *ndp; | |
758 | struct proc *p; | |
759 | { | |
760 | struct denode *pdep = VTODE(ndp->ni_dvp); | |
761 | ||
762 | free(ndp->ni_pnbuf, M_NAMEI); | |
763 | deput(pdep); | |
764 | return EINVAL; | |
765 | } | |
766 | ||
767 | /* | |
768 | * Renames on files require moving the denode to a new hash queue since the | |
769 | * denode's location is used to compute which hash queue to put the file | |
770 | * in. Unless it is a rename in place. For example "mv a b". | |
771 | * | |
772 | * What follows is the basic algorithm: | |
773 | * | |
774 | * if (file move) { if (dest file exists) { remove dest file } if (dest and | |
775 | * src in same directory) { rewrite name in existing directory slot } else | |
776 | * { write new entry in dest directory update offset and dirclust in denode | |
777 | * move denode to new hash chain clear old directory entry } } else { | |
778 | * directory move if (dest directory exists) { if (dest is not empty) { | |
779 | * return ENOTEMPTY } remove dest directory } if (dest and src in same | |
780 | * directory) { rewrite name in existing entry } else { be sure dest is not | |
781 | * a child of src directory write entry in dest directory update "." and | |
782 | * ".." in moved directory update offset and dirclust in denode move denode | |
783 | * to new hash chain clear old directory entry for moved directory } } | |
784 | * | |
785 | * On entry: source's parent directory is unlocked source file or directory is | |
786 | * unlocked destination's parent directory is locked destination file or | |
787 | * directory is locked if it exists | |
788 | * | |
789 | * On exit: all denodes should be released Notes: I'm not sure how the memory | |
790 | * containing the pathnames pointed at by the nameidata structures is | |
791 | * freed, there may be some memory bleeding for each rename done. | |
792 | */ | |
793 | int | |
794 | msdosfs_rename(fndp, tndp, p) | |
795 | struct nameidata *fndp; | |
796 | struct nameidata *tndp; | |
797 | struct proc *p; | |
798 | { | |
799 | u_char toname[11]; | |
800 | int error; | |
801 | int newparent = 0; | |
802 | int sourceisadirectory = 0; | |
803 | u_long to_dirclust; | |
804 | u_long to_diroffset; | |
805 | u_long cn; | |
806 | daddr_t bn; | |
807 | struct denode *fddep; /* from file's parent directory */ | |
808 | struct denode *fdep; /* from file or directory */ | |
809 | struct denode *tddep; /* to file's parent directory */ | |
810 | struct denode *tdep; /* to file or directory */ | |
811 | struct msdosfsmount *pmp; | |
812 | struct direntry *dotdotp; | |
813 | struct direntry *ep; | |
814 | struct buf *bp; | |
815 | ||
816 | #if defined(MSDOSFSDEBUG) | |
817 | printf("msdosfs_rename(fndp %08x, tndp %08x, p %08x\n", fndp, tndp, p); | |
818 | #endif /* defined(MSDOSFSDEBUG) */ | |
819 | fddep = VTODE(fndp->ni_dvp); | |
820 | fdep = VTODE(fndp->ni_vp); | |
821 | tddep = VTODE(tndp->ni_dvp); | |
822 | tdep = tndp->ni_vp ? VTODE(tndp->ni_vp) : NULL; | |
823 | pmp = fddep->de_pmp; | |
824 | ||
825 | #ifdef __NetBSD__ | |
826 | /* Check for cross-device rename */ | |
827 | if ((fndp->ni_vp->v_mount != tndp->ni_dvp->v_mount) || | |
828 | (tndp->ni_vp && (fndp->ni_vp->v_mount != tndp->ni_vp->v_mount))) { | |
829 | error = EXDEV; | |
830 | goto bad; | |
831 | } | |
832 | #endif | |
833 | ||
834 | /* | |
835 | * Convert the filename in tdnp into a dos filename. We copy this | |
836 | * into the denode and directory entry for the destination | |
837 | * file/directory. | |
838 | */ | |
839 | unix2dosfn((u_char *) tndp->ni_ptr, toname, tndp->ni_namelen); | |
840 | ||
841 | /* | |
842 | * At this point this is the lock state of the denodes: fddep | |
843 | * referenced fdep referenced tddep locked tdep locked if it | |
844 | * exists | |
845 | */ | |
846 | ||
847 | /* | |
848 | * Be sure we are not renaming ".", "..", or an alias of ".". This | |
849 | * leads to a crippled directory tree. It's pretty tough to do a | |
850 | * "ls" or "pwd" with the "." directory entry missing, and "cd .." | |
851 | * doesn't work if the ".." entry is missing. | |
852 | */ | |
853 | if (fdep->de_Attributes & ATTR_DIRECTORY) { | |
854 | if ((fndp->ni_namelen == 1 && fndp->ni_ptr[0] == '.') || | |
855 | fddep == fdep || /* won't happen ? */ | |
856 | fndp->ni_isdotdot) { | |
857 | VOP_ABORTOP(tndp); | |
858 | vput(tndp->ni_dvp); | |
859 | if (tndp->ni_vp) | |
860 | vput(tndp->ni_vp); | |
861 | VOP_ABORTOP(fndp); | |
862 | vrele(fndp->ni_dvp); | |
863 | vrele(fndp->ni_vp); | |
864 | return EINVAL; | |
865 | } | |
866 | sourceisadirectory = 1; | |
867 | } | |
868 | ||
869 | /* | |
870 | * If we are renaming a directory, and the directory is being moved | |
871 | * to another directory, then we must be sure the destination | |
872 | * directory is not in the subtree of the source directory. This | |
873 | * could orphan everything under the source directory. | |
874 | * doscheckpath() unlocks the destination's parent directory so we | |
875 | * must look it up again to relock it. | |
876 | */ | |
877 | if (fddep->de_StartCluster != tddep->de_StartCluster) | |
878 | newparent = 1; | |
879 | if (sourceisadirectory && newparent) { | |
880 | if (tdep) { | |
881 | deput(tdep); | |
882 | tdep = NULL; | |
883 | } | |
884 | /* doscheckpath() deput()'s tddep */ | |
885 | error = doscheckpath(fdep, tddep, tndp->ni_cred); | |
886 | tddep = NULL; | |
887 | if (error) { | |
888 | goto bad; | |
889 | } | |
890 | if ((tndp->ni_nameiop & SAVESTART) == 0) | |
891 | panic("msdosfs_rename(): lost to startdir"); | |
892 | if (error = lookup(tndp, p)) { | |
893 | goto bad; | |
894 | } | |
895 | tddep = VTODE(tndp->ni_dvp); | |
896 | tdep = tndp->ni_vp ? VTODE(tndp->ni_vp) : NULL; | |
897 | } | |
898 | ||
899 | /* | |
900 | * If the destination exists, then be sure its type (file or dir) | |
901 | * matches that of the source. And, if it is a directory make sure | |
902 | * it is empty. Then delete the destination. | |
903 | */ | |
904 | if (tdep) { | |
905 | if (tdep->de_Attributes & ATTR_DIRECTORY) { | |
906 | if (!sourceisadirectory) { | |
907 | error = ENOTDIR; | |
908 | goto bad; | |
909 | } | |
910 | if (!dosdirempty(tdep)) { | |
911 | error = ENOTEMPTY; | |
912 | goto bad; | |
913 | } | |
914 | } | |
915 | else { /* destination is file */ | |
916 | if (sourceisadirectory) { | |
917 | error = EISDIR; | |
918 | goto bad; | |
919 | } | |
920 | } | |
921 | to_dirclust = tdep->de_dirclust; | |
922 | to_diroffset = tdep->de_diroffset; | |
923 | if (error = removede(tndp)) { | |
924 | goto bad; | |
925 | } | |
926 | deput(tdep); | |
927 | tdep = NULL; | |
928 | ||
929 | /* | |
930 | * Remember where the slot was for createde(). | |
931 | */ | |
932 | tndp->ni_msdosfs.msdosfs_count = 1; | |
933 | tndp->ni_msdosfs.msdosfs_cluster = to_dirclust; | |
934 | tndp->ni_msdosfs.msdosfs_offset = to_diroffset; | |
935 | } | |
936 | ||
937 | /* | |
938 | * If the source and destination are in the same directory then | |
939 | * just read in the directory entry, change the name in the | |
940 | * directory entry and write it back to disk. | |
941 | */ | |
942 | if (newparent == 0) { | |
943 | /* tddep and fddep point to the same denode here */ | |
944 | DELOCK(fdep); /* tddep is already locked */ | |
945 | if (error = readep(fdep->de_pmp, | |
946 | fndp->ni_msdosfs.msdosfs_cluster, | |
947 | fndp->ni_msdosfs.msdosfs_offset, | |
948 | &bp, &ep)) { | |
949 | DEUNLOCK(fdep); | |
950 | goto bad; | |
951 | } | |
952 | bcopy(toname, ep->deName, 11); | |
953 | if (error = bwrite(bp)) { | |
954 | DEUNLOCK(fdep); | |
955 | goto bad; | |
956 | } | |
957 | bcopy(toname, fdep->de_Name, 11); /* update denode */ | |
958 | /* | |
959 | * fdep locked fddep and tddep point to the same denode | |
960 | * which is locked tdep is unlocked and unreferenced | |
961 | */ | |
962 | } | |
963 | else { | |
964 | u_long dirsize; | |
965 | ||
966 | /* | |
967 | * If the source and destination are in different | |
968 | * directories, then mark the entry in the source directory | |
969 | * as deleted and write a new entry in the destination | |
970 | * directory. Then move the denode to the correct hash | |
971 | * chain for its new location in the filesystem. And, if | |
972 | * we moved a directory, then update its .. entry to point | |
973 | * to the new parent directory. If we moved a directory | |
974 | * will also insure that the directory entry on disk has a | |
975 | * filesize of zero. | |
976 | */ | |
977 | DELOCK(fdep); | |
978 | bcopy(toname, fdep->de_Name, 11); /* update denode */ | |
979 | if (fdep->de_Attributes & ATTR_DIRECTORY) { | |
980 | dirsize = fdep->de_FileSize; | |
981 | fdep->de_FileSize = 0; | |
982 | } | |
983 | error = createde(fdep, tndp, (struct denode **) 0); | |
984 | if (fdep->de_Attributes & ATTR_DIRECTORY) { | |
985 | fdep->de_FileSize = dirsize; | |
986 | } | |
987 | if (error) { | |
988 | /* should put back filename */ | |
989 | DEUNLOCK(fdep); | |
990 | goto bad; | |
991 | } | |
992 | DELOCK(fddep); | |
993 | if (error = readep(fdep->de_pmp, | |
994 | fndp->ni_msdosfs.msdosfs_cluster, | |
995 | fndp->ni_msdosfs.msdosfs_offset, | |
996 | &bp, &ep)) { | |
997 | DEUNLOCK(fdep); | |
998 | DEUNLOCK(fddep); | |
999 | goto bad; | |
1000 | } | |
1001 | ep->deName[0] = SLOT_DELETED; | |
1002 | if (error = bwrite(bp)) { | |
1003 | DEUNLOCK(fdep); | |
1004 | DEUNLOCK(fddep); | |
1005 | goto bad; | |
1006 | } | |
1007 | fdep->de_dirclust = tndp->ni_msdosfs.msdosfs_cluster; | |
1008 | fdep->de_diroffset = tndp->ni_msdosfs.msdosfs_offset; | |
1009 | reinsert(fdep); | |
1010 | DEUNLOCK(fddep); | |
1011 | } | |
1012 | /* fdep is still locked here */ | |
1013 | ||
1014 | /* | |
1015 | * If we moved a directory to a new parent directory, then we must | |
1016 | * fixup the ".." entry in the moved directory. | |
1017 | */ | |
1018 | if (sourceisadirectory && newparent) { | |
1019 | cn = fdep->de_StartCluster; | |
1020 | if (cn == MSDOSFSROOT) { | |
1021 | /* this should never happen */ | |
1022 | panic("msdosfs_rename(): updating .. in root directory?\n"); | |
1023 | } | |
1024 | else { | |
1025 | bn = cntobn(pmp, cn); | |
1026 | } | |
1027 | error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, | |
1028 | NOCRED, &bp); | |
1029 | if (error) { | |
1030 | /* should really panic here, fs is corrupt */ | |
1031 | DEUNLOCK(fdep); | |
1032 | goto bad; | |
1033 | } | |
1034 | dotdotp = (struct direntry *) bp->b_un.b_addr + 1; | |
1035 | dotdotp->deStartCluster = tddep->de_StartCluster; | |
1036 | error = bwrite(bp); | |
1037 | DEUNLOCK(fdep); | |
1038 | if (error) { | |
1039 | /* should really panic here, fs is corrupt */ | |
1040 | goto bad; | |
1041 | } | |
1042 | } | |
1043 | else { | |
1044 | DEUNLOCK(fdep); | |
1045 | } | |
1046 | bad: ; | |
1047 | vrele(DETOV(fdep)); | |
1048 | vrele(DETOV(fddep)); | |
1049 | if (tdep) | |
1050 | vput(DETOV(tdep)); | |
1051 | if (tddep) | |
1052 | vput(DETOV(tddep)); | |
1053 | return error; | |
1054 | } | |
1055 | ||
1056 | struct { | |
1057 | struct direntry dot; | |
1058 | struct direntry dotdot; | |
1059 | } dosdirtemplate = { | |
1060 | ||
1061 | ". ", " ", /* the . entry */ | |
1062 | ATTR_DIRECTORY, /* file attribute */ | |
1063 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* resevered */ | |
1064 | 1234, 1234, /* time and date */ | |
1065 | 0, /* startcluster */ | |
1066 | 0, /* filesize */ | |
1067 | ".. ", " ", /* the .. entry */ | |
1068 | ATTR_DIRECTORY, /* file attribute */ | |
1069 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* resevered */ | |
1070 | 1234, 1234, /* time and date */ | |
1071 | 0, /* startcluster */ | |
1072 | 0, /* filesize */ | |
1073 | }; | |
1074 | ||
1075 | int | |
1076 | msdosfs_mkdir(ndp, vap, p) | |
1077 | struct nameidata *ndp; | |
1078 | struct vattr *vap; | |
1079 | struct proc *p; | |
1080 | { | |
1081 | int bn; | |
1082 | int error; | |
1083 | u_long newcluster; | |
1084 | struct denode *pdep; | |
1085 | struct denode *ndep; | |
1086 | struct vnode *pvp; | |
1087 | struct direntry *denp; | |
1088 | struct denode ndirent; | |
1089 | struct msdosfsmount *pmp; | |
1090 | struct buf *bp; | |
1091 | ||
1092 | pvp = ndp->ni_dvp; | |
1093 | pdep = VTODE(pvp); | |
1094 | ||
1095 | /* | |
1096 | * If this is the root directory and there is no space left we | |
1097 | * can't do anything. This is because the root directory can not | |
1098 | * change size. | |
1099 | */ | |
1100 | if (pdep->de_StartCluster == MSDOSFSROOT && ndp->ni_msdosfs.msdosfs_count == 0) { | |
1101 | free(ndp->ni_pnbuf, M_NAMEI); | |
1102 | deput(pdep); | |
1103 | return ENOSPC; | |
1104 | } | |
1105 | ||
1106 | pmp = pdep->de_pmp; | |
1107 | ||
1108 | /* | |
1109 | * Allocate a cluster to hold the about to be created directory. | |
1110 | */ | |
1111 | if (error = clusteralloc(pmp, &newcluster, CLUST_EOFE)) { | |
1112 | free(ndp->ni_pnbuf, M_NAMEI); | |
1113 | deput(pdep); | |
1114 | return error; | |
1115 | } | |
1116 | ||
1117 | /* | |
1118 | * Now fill the cluster with the "." and ".." entries. And write | |
1119 | * the cluster to disk. This way it is there for the parent | |
1120 | * directory to be pointing at if there were a crash. | |
1121 | */ | |
1122 | bn = cntobn(pmp, newcluster); | |
1123 | bp = getblk(pmp->pm_devvp, bn, pmp->pm_bpcluster); /* always succeeds */ | |
1124 | bzero(bp->b_un.b_addr, pmp->pm_bpcluster); | |
1125 | bcopy(&dosdirtemplate, bp->b_un.b_addr, sizeof dosdirtemplate); | |
1126 | denp = (struct direntry *) bp->b_un.b_addr; | |
1127 | denp->deStartCluster = newcluster; | |
1128 | unix2dostime(&time, (union dosdate *) & denp->deDate, | |
1129 | (union dostime *) & denp->deTime); | |
1130 | denp++; | |
1131 | denp->deStartCluster = pdep->de_StartCluster; | |
1132 | unix2dostime(&time, (union dosdate *) & denp->deDate, | |
1133 | (union dostime *) & denp->deTime); | |
1134 | if (error = bwrite(bp)) { | |
1135 | clusterfree(pmp, newcluster, NULL); | |
1136 | free(ndp->ni_pnbuf, M_NAMEI); | |
1137 | deput(pdep); | |
1138 | return error; | |
1139 | } | |
1140 | ||
1141 | /* | |
1142 | * Now build up a directory entry pointing to the newly allocated | |
1143 | * cluster. This will be written to an empty slot in the parent | |
1144 | * directory. | |
1145 | */ | |
1146 | ndep = &ndirent; | |
1147 | bzero(ndep, sizeof(*ndep)); | |
1148 | unix2dosfn((u_char *) ndp->ni_ptr, ndep->de_Name, ndp->ni_namelen); | |
1149 | unix2dostime(&time, (union dosdate *) & ndep->de_Date, | |
1150 | (union dostime *) & ndep->de_Time); | |
1151 | ndep->de_StartCluster = newcluster; | |
1152 | ndep->de_Attributes = ATTR_DIRECTORY; | |
1153 | ndep->de_pmp = pmp; /* createde() needs this */ | |
1154 | ||
1155 | error = createde(ndep, ndp, &ndep); | |
1156 | if (error) { | |
1157 | clusterfree(pmp, newcluster, NULL); | |
1158 | } | |
1159 | else { | |
1160 | ndp->ni_vp = DETOV(ndep); | |
1161 | } | |
1162 | free(ndp->ni_pnbuf, M_NAMEI); | |
1163 | #if defined(MSDOSFSDEBUG) | |
1164 | printf("msdosfs_mkdir(): deput(%08x), vnode %08x\n", pdep, DETOV(pdep)); | |
1165 | #endif /* defined(MSDOSFSDEBUG) */ | |
1166 | deput(pdep); | |
1167 | return error; | |
1168 | } | |
1169 | ||
1170 | int | |
1171 | msdosfs_rmdir(ndp, p) | |
1172 | struct nameidata *ndp; | |
1173 | struct proc *p; | |
1174 | { | |
1175 | struct denode *ddep; | |
1176 | struct denode *dep; | |
1177 | int error = 0; | |
1178 | ||
1179 | ddep = VTODE(ndp->ni_dvp); /* parent dir of dir to delete */ | |
1180 | dep = VTODE(ndp->ni_vp);/* directory to delete */ | |
1181 | ||
1182 | /* | |
1183 | * Don't let "rmdir ." go thru. | |
1184 | */ | |
1185 | if (ddep == dep) { | |
1186 | vrele(DETOV(dep)); | |
1187 | deput(dep); | |
1188 | return EINVAL; | |
1189 | } | |
1190 | ||
1191 | /* | |
1192 | * Be sure the directory being deleted is empty. | |
1193 | */ | |
1194 | if (dosdirempty(dep) == 0) { | |
1195 | error = ENOTEMPTY; | |
1196 | goto out; | |
1197 | } | |
1198 | ||
1199 | /* | |
1200 | * Delete the entry from the directory. For dos filesystems this | |
1201 | * gets rid of the directory entry on disk, the in memory copy | |
1202 | * still exists but the de_refcnt is <= 0. This prevents it from | |
1203 | * being found by deget(). When the deput() on dep is done we give | |
1204 | * up access and eventually msdosfs_reclaim() will be called which | |
1205 | * will remove it from the denode cache. | |
1206 | */ | |
1207 | if (error = removede(ndp)) | |
1208 | goto out; | |
1209 | ||
1210 | /* | |
1211 | * This is where we decrement the link count in the parent | |
1212 | * directory. Since dos filesystems don't do this we just purge | |
1213 | * the name cache and let go of the parent directory denode. | |
1214 | */ | |
1215 | cache_purge(DETOV(ddep)); | |
1216 | deput(ddep); | |
1217 | ndp->ni_dvp = NULL; /* getting rid of parent dir pointer? */ | |
1218 | ||
1219 | /* | |
1220 | * Truncate the directory that is being deleted. | |
1221 | */ | |
1222 | error = detrunc(dep, (u_long) 0, IO_SYNC); | |
1223 | cache_purge(DETOV(dep)); | |
1224 | ||
1225 | out: ; | |
1226 | if (ndp->ni_dvp) | |
1227 | deput(ddep); | |
1228 | deput(dep); | |
1229 | return error; | |
1230 | } | |
1231 | ||
1232 | /* | |
1233 | * DOS filesystems don't know what symlinks are. | |
1234 | */ | |
1235 | int | |
1236 | msdosfs_symlink(ndp, vap, target, p) | |
1237 | struct nameidata *ndp; | |
1238 | struct vattr *vap; | |
1239 | char *target; | |
1240 | struct proc *p; | |
1241 | { | |
1242 | struct denode *pdep = VTODE(ndp->ni_dvp); | |
1243 | ||
1244 | free(ndp->ni_pnbuf, M_NAMEI); | |
1245 | deput(pdep); | |
1246 | return EINVAL; | |
1247 | } | |
1248 | ||
1249 | /* | |
1250 | * Dummy dirents to simulate the "." and ".." entries of the root directory | |
1251 | * in a dos filesystem. Dos doesn't provide these. Note that each entry | |
1252 | * must be the same size as a dos directory entry (32 bytes). | |
1253 | */ | |
1254 | struct dos_dirent { | |
1255 | u_long d_fileno; | |
1256 | u_short d_reclen; | |
1257 | u_short d_namlen; | |
1258 | u_char d_name[24]; | |
1259 | } rootdots[2] = { | |
1260 | ||
1261 | { | |
1262 | 1, /* d_fileno */ | |
1263 | sizeof(struct direntry), /* d_reclen */ | |
1264 | 1, /* d_namlen */ | |
1265 | "." /* d_name */ | |
1266 | }, | |
1267 | { | |
1268 | 1, /* d_fileno */ | |
1269 | sizeof(struct direntry), /* d_reclen */ | |
1270 | 2, /* d_namlen */ | |
1271 | ".." /* d_name */ | |
1272 | } | |
1273 | }; | |
1274 | ||
1275 | int | |
1276 | msdosfs_readdir(vp, uio, cred, eofflagp, cookies, ncookies) | |
1277 | struct vnode *vp; | |
1278 | struct uio *uio; | |
1279 | struct ucred *cred; | |
1280 | int *eofflagp; | |
1281 | u_int *cookies; | |
1282 | int ncookies; | |
1283 | { | |
1284 | int error = 0; | |
1285 | int diff; | |
1286 | char pushout; | |
1287 | long n; | |
1288 | long on; | |
1289 | long lost; | |
1290 | long count; | |
1291 | u_long cn; | |
1292 | u_long fileno; | |
1293 | long bias = 0; | |
1294 | daddr_t bn; | |
1295 | daddr_t lbn; | |
1296 | struct buf *bp; | |
1297 | struct denode *dep = VTODE(vp); | |
1298 | struct msdosfsmount *pmp = dep->de_pmp; | |
1299 | struct direntry *dentp; | |
1300 | struct dirent *prev; | |
1301 | struct dirent *crnt; | |
1302 | u_char dirbuf[512]; /* holds converted dos directories */ | |
1303 | int i = 0; | |
1304 | ||
1305 | #if defined(MSDOSFSDEBUG) | |
1306 | printf("msdosfs_readdir(): vp %08x, uio %08x, cred %08x, eofflagp %08x\n", | |
1307 | vp, uio, cred, eofflagp); | |
1308 | #endif /* defined(MSDOSFSDEBUG) */ | |
1309 | ||
1310 | if (!cookies) | |
1311 | ncookies = 1; | |
1312 | ||
1313 | /* | |
1314 | * msdosfs_readdir() won't operate properly on regular files since | |
1315 | * it does i/o only with the the filesystem vnode, and hence can | |
1316 | * retrieve the wrong block from the buffer cache for a plain file. | |
1317 | * So, fail attempts to readdir() on a plain file. | |
1318 | */ | |
1319 | if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) | |
1320 | return ENOTDIR; | |
1321 | ||
1322 | /* | |
1323 | * If the user buffer is smaller than the size of one dos directory | |
1324 | * entry or the file offset is not a multiple of the size of a | |
1325 | * directory entry, then we fail the read. | |
1326 | */ | |
1327 | count = uio->uio_resid & ~(sizeof(struct direntry) - 1); | |
1328 | lost = uio->uio_resid - count; | |
1329 | if (count < sizeof(struct direntry) || | |
1330 | (uio->uio_offset & (sizeof(struct direntry) - 1))) | |
1331 | return EINVAL; | |
1332 | uio->uio_resid = count; | |
1333 | uio->uio_iov->iov_len = count; | |
1334 | ||
1335 | /* | |
1336 | * If they are reading from the root directory then, we simulate | |
1337 | * the . and .. entries since these don't exist in the root | |
1338 | * directory. We also set the offset bias to make up for having to | |
1339 | * simulate these entries. By this I mean that at file offset 64 we | |
1340 | * read the first entry in the root directory that lives on disk. | |
1341 | */ | |
1342 | if (dep->de_StartCluster == MSDOSFSROOT) { | |
1343 | /* | |
1344 | * printf("msdosfs_readdir(): going after . or .. in root | |
1345 | * dir, offset %d\n", uio->uio_offset); | |
1346 | */ | |
1347 | bias = 2 * sizeof(struct direntry); | |
1348 | if (uio->uio_offset < 2 * sizeof(struct direntry)) { | |
1349 | if (uio->uio_offset | |
1350 | && uio->uio_offset != sizeof(struct direntry)) { | |
1351 | error = EINVAL; | |
1352 | goto out; | |
1353 | } | |
1354 | n = 1; | |
1355 | if (!uio->uio_offset) { | |
1356 | n = 2; | |
1357 | if (cookies) { | |
1358 | *cookies++ = sizeof(struct direntry); | |
1359 | ncookies--; | |
1360 | } | |
1361 | } | |
1362 | if (cookies) { | |
1363 | if (ncookies-- <= 0) | |
1364 | n--; | |
1365 | else | |
1366 | *cookies++ = 2 * sizeof(struct direntry); | |
1367 | } | |
1368 | ||
1369 | error = uiomove((char *) rootdots + uio->uio_offset, | |
1370 | n * sizeof(struct direntry), uio); | |
1371 | } | |
1372 | } | |
1373 | while (!error && uio->uio_resid > 0 && ncookies > 0) { | |
1374 | lbn = (uio->uio_offset - bias) >> pmp->pm_cnshift; | |
1375 | on = (uio->uio_offset - bias) & pmp->pm_crbomask; | |
1376 | n = MIN((u_long) (pmp->pm_bpcluster - on), uio->uio_resid); | |
1377 | diff = dep->de_FileSize - (uio->uio_offset - bias); | |
1378 | if (diff <= 0) | |
1379 | return 0; | |
1380 | if (diff < n) | |
1381 | n = diff; | |
1382 | error = pcbmap(dep, lbn, &bn, &cn); | |
1383 | if (error) | |
1384 | break; | |
1385 | error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, &bp); | |
1386 | n = MIN(n, pmp->pm_bpcluster - bp->b_resid); | |
1387 | if (error) { | |
1388 | brelse(bp); | |
1389 | return error; | |
1390 | } | |
1391 | ||
1392 | /* | |
1393 | * code to convert from dos directory entries to ufs | |
1394 | * directory entries | |
1395 | */ | |
1396 | pushout = 0; | |
1397 | dentp = (struct direntry *) (bp->b_un.b_addr + on); | |
1398 | prev = 0; | |
1399 | crnt = (struct dirent *) dirbuf; | |
1400 | while ((char *) dentp < bp->b_un.b_addr + on + n) { | |
1401 | /* | |
1402 | * printf("rd: dentp %08x prev %08x crnt %08x | |
1403 | * deName %02x attr %02x\n", dentp, prev, crnt, | |
1404 | * dentp->deName[0], dentp->deAttributes); | |
1405 | */ | |
1406 | /* | |
1407 | * If we have an empty entry or a slot from a | |
1408 | * deleted file, or a volume label entry just | |
1409 | * concatenate its space onto the end of the | |
1410 | * previous entry or, manufacture an empty entry if | |
1411 | * there is no previous entry. | |
1412 | */ | |
1413 | if (dentp->deName[0] == SLOT_EMPTY || | |
1414 | dentp->deName[0] == SLOT_DELETED || | |
1415 | (dentp->deAttributes & ATTR_VOLUME)) { | |
1416 | if (prev) { | |
1417 | prev->d_reclen += sizeof(struct direntry); | |
1418 | if (cookies) { | |
1419 | ncookies++; | |
1420 | cookies--; | |
1421 | } | |
1422 | } | |
1423 | else { | |
1424 | prev = crnt; | |
1425 | prev->d_fileno = 0; | |
1426 | prev->d_reclen = sizeof(struct direntry); | |
1427 | prev->d_namlen = 0; | |
1428 | prev->d_name[0] = 0; | |
1429 | } | |
1430 | } | |
1431 | else { | |
1432 | /* | |
1433 | * this computation of d_fileno must match | |
1434 | * the computation of va_fileid in | |
1435 | * msdosfs_getattr | |
1436 | */ | |
1437 | if (dentp->deAttributes & ATTR_DIRECTORY) { | |
1438 | /* if this is the root directory */ | |
1439 | if ((fileno = dentp->deStartCluster) == MSDOSFSROOT) | |
1440 | fileno = 1; | |
1441 | } | |
1442 | else { | |
1443 | /* | |
1444 | * if the file's dirent lives in | |
1445 | * root dir | |
1446 | */ | |
1447 | if ((fileno = cn) == MSDOSFSROOT) | |
1448 | fileno = 1; | |
1449 | fileno = (fileno << 16) | | |
1450 | ((dentp - (struct direntry *) bp->b_un.b_addr) & 0xffff); | |
1451 | } | |
1452 | crnt->d_fileno = fileno; | |
1453 | crnt->d_reclen = sizeof(struct direntry); | |
1454 | crnt->d_namlen = dos2unixfn(dentp->deName, | |
1455 | (u_char *) crnt->d_name); | |
1456 | /* | |
1457 | * printf("readdir: file %s, fileno %08x, | |
1458 | * attr %02x, start %08x\n", crnt->d_name, | |
1459 | * crnt->d_fileno, dentp->deAttributes, | |
1460 | * dentp->deStartCluster); | |
1461 | */ | |
1462 | prev = crnt; | |
1463 | } | |
1464 | dentp++; | |
1465 | if (cookies) { | |
1466 | *cookies++ = (u_int)((char *)dentp - bp->b_un.b_addr - on) | |
1467 | + uio->uio_offset; | |
1468 | ncookies--; | |
1469 | } | |
1470 | ||
1471 | crnt = (struct dirent *) ((char *) crnt + sizeof(struct direntry)); | |
1472 | pushout = 1; | |
1473 | ||
1474 | /* | |
1475 | * If our intermediate buffer is full then copy its | |
1476 | * contents to user space. I would just use the | |
1477 | * buffer the buf header points to but, I'm afraid | |
1478 | * that when we brelse() it someone else might find | |
1479 | * it in the cache and think its contents are | |
1480 | * valid. Maybe there is a way to invalidate the | |
1481 | * buffer before brelse()'ing it. | |
1482 | */ | |
1483 | if ((u_char *) crnt >= &dirbuf[sizeof dirbuf]) { | |
1484 | pushout = 0; | |
1485 | error = uiomove(dirbuf, sizeof(dirbuf), uio); | |
1486 | if (error) | |
1487 | break; | |
1488 | prev = 0; | |
1489 | crnt = (struct dirent *) dirbuf; | |
1490 | } | |
1491 | if (ncookies <= 0) | |
1492 | break; | |
1493 | } | |
1494 | if (pushout) { | |
1495 | pushout = 0; | |
1496 | error = uiomove(dirbuf, (char *) crnt - (char *) dirbuf, | |
1497 | uio); | |
1498 | } | |
1499 | ||
1500 | #if 0 | |
1501 | /* | |
1502 | * If we have read everything from this block or have read | |
1503 | * to end of file then we are done with this block. Mark | |
1504 | * it to say the buffer can be reused if need be. | |
1505 | */ | |
1506 | if (n + on == pmp->pm_bpcluster || | |
1507 | (uio->uio_offset - bias) == dep->de_FileSize) | |
1508 | bp->b_flags |= B_AGE; | |
1509 | #endif /* if 0 */ | |
1510 | brelse(bp); | |
1511 | if (n == 0) | |
1512 | break; | |
1513 | } | |
1514 | out: ; | |
1515 | uio->uio_resid += lost; | |
1516 | ||
1517 | /* | |
1518 | * I don't know why we bother setting this eofflag, getdirentries() | |
1519 | * in vfs_syscalls.c doesn't bother to look at it when we return. | |
1520 | * (because NFS uses it in nfs_serv.c -- JMP) | |
1521 | */ | |
1522 | if (dep->de_FileSize - uio->uio_offset - bias <= 0) | |
1523 | *eofflagp = 1; | |
1524 | else | |
1525 | *eofflagp = 0; | |
1526 | return error; | |
1527 | } | |
1528 | ||
1529 | /* | |
1530 | * DOS filesystems don't know what symlinks are. | |
1531 | */ | |
1532 | int | |
1533 | msdosfs_readlink(vp, uio, cred) | |
1534 | struct vnode *vp; | |
1535 | struct uio *uio; | |
1536 | struct ucred *cred; | |
1537 | { | |
1538 | return EINVAL; | |
1539 | } | |
1540 | ||
1541 | int | |
1542 | msdosfs_abortop(ndp) | |
1543 | struct nameidata *ndp; | |
1544 | { | |
1545 | if ((ndp->ni_nameiop & (HASBUF | SAVESTART)) == HASBUF) | |
1546 | FREE(ndp->ni_pnbuf, M_NAMEI); | |
1547 | return 0; | |
1548 | } | |
1549 | ||
1550 | int | |
1551 | msdosfs_lock(vp) | |
1552 | struct vnode *vp; | |
1553 | { | |
1554 | struct denode *dep = VTODE(vp); | |
1555 | ||
1556 | DELOCK(dep); | |
1557 | return 0; | |
1558 | } | |
1559 | ||
1560 | int | |
1561 | msdosfs_unlock(vp) | |
1562 | struct vnode *vp; | |
1563 | { | |
1564 | struct denode *dep = VTODE(vp); | |
1565 | ||
1566 | if (!(dep->de_flag & DELOCKED)) | |
1567 | panic("msdosfs_unlock: denode not locked"); | |
1568 | DEUNLOCK(dep); | |
1569 | return 0; | |
1570 | } | |
1571 | ||
1572 | int | |
1573 | msdosfs_islocked(vp) | |
1574 | struct vnode *vp; | |
1575 | { | |
1576 | return VTODE(vp)->de_flag & DELOCKED ? 1 : 0; | |
1577 | } | |
1578 | ||
1579 | /* | |
1580 | * vp - address of vnode file the file bn - which cluster we are interested | |
1581 | * in mapping to a filesystem block number. vpp - returns the vnode for the | |
1582 | * block special file holding the filesystem containing the file of | |
1583 | * interest bnp - address of where to return the filesystem relative block | |
1584 | * number | |
1585 | */ | |
1586 | int | |
1587 | msdosfs_bmap(vp, bn, vpp, bnp) | |
1588 | struct vnode *vp; | |
1589 | daddr_t bn; | |
1590 | struct vnode **vpp; | |
1591 | daddr_t *bnp; | |
1592 | { | |
1593 | struct denode *dep = VTODE(vp); | |
1594 | struct msdosfsmount *pmp = dep->de_pmp; | |
1595 | ||
1596 | if (vpp != NULL) | |
1597 | *vpp = dep->de_devvp; | |
1598 | if (bnp == NULL) | |
1599 | return 0; | |
1600 | return pcbmap(dep, bn << (pmp->pm_cnshift - pmp->pm_bnshift), bnp, 0); | |
1601 | } | |
1602 | ||
1603 | int | |
1604 | msdosfs_strategy(bp) | |
1605 | struct buf *bp; | |
1606 | { | |
1607 | struct denode *dep = VTODE(bp->b_vp); | |
1608 | struct msdosfsmount *pmp = dep->de_pmp; | |
1609 | struct vnode *vp; | |
1610 | int error; | |
1611 | ||
1612 | if (bp->b_vp->v_type == VBLK || bp->b_vp->v_type == VCHR) | |
1613 | panic("msdosfs_strategy: spec"); | |
1614 | /* | |
1615 | * If we don't already know the filesystem relative block number | |
1616 | * then get it using pcbmap(). If pcbmap() returns the block | |
1617 | * number as -1 then we've got a hole in the file. DOS filesystems | |
1618 | * don't allow files with holes, so we shouldn't ever see this. | |
1619 | */ | |
1620 | if (bp->b_blkno == bp->b_lblkno) { | |
1621 | if (error = pcbmap(dep, bp->b_lblkno, &bp->b_blkno, 0)) | |
1622 | return error; | |
1623 | if ((long) bp->b_blkno == -1) | |
1624 | clrbuf(bp); | |
1625 | } | |
1626 | if ((long) bp->b_blkno == -1) { | |
1627 | biodone(bp); | |
1628 | return 0; | |
1629 | } | |
1630 | #ifdef DIAGNOSTIC | |
1631 | #endif /* defined(DIAGNOSTIC) */ | |
1632 | /* | |
1633 | * Read/write the block from/to the disk that contains the desired | |
1634 | * file block. | |
1635 | */ | |
1636 | vp = dep->de_devvp; | |
1637 | bp->b_dev = vp->v_rdev; | |
1638 | (*(vp->v_op->vop_strategy)) (bp); | |
1639 | return 0; | |
1640 | } | |
1641 | ||
1642 | int | |
1643 | msdosfs_print(vp) | |
1644 | struct vnode *vp; | |
1645 | { | |
1646 | struct denode *dep = VTODE(vp); | |
1647 | ||
1648 | printf("tag VT_MSDOSFS, startcluster %d, dircluster %d, diroffset %d ", | |
1649 | dep->de_StartCluster, dep->de_dirclust, dep->de_diroffset); | |
1650 | printf(" dev %d, %d, %s\n", | |
1651 | major(dep->de_dev), minor(dep->de_dev), | |
1652 | dep->de_flag & DELOCKED ? "(LOCKED)" : ""); | |
1653 | if (dep->de_spare0) { | |
1654 | printf(" owner pid %d", dep->de_spare0); | |
1655 | if (dep->de_spare1) | |
1656 | printf(" waiting pid %d", dep->de_spare1); | |
1657 | printf("\n"); | |
1658 | } | |
1659 | } | |
1660 | ||
1661 | int | |
1662 | msdosfs_advlock(vp, id, op, fl, flags) | |
1663 | struct vnode *vp; | |
1664 | caddr_t id; | |
1665 | int op; | |
1666 | struct flock *fl; | |
1667 | int flags; | |
1668 | { | |
1669 | return EINVAL; /* we don't do locking yet */ | |
1670 | } | |
1671 | ||
1672 | struct vnodeops msdosfs_vnodeops = { | |
1673 | msdosfs_lookup, | |
1674 | msdosfs_create, | |
1675 | msdosfs_mknod, | |
1676 | msdosfs_open, | |
1677 | msdosfs_close, | |
1678 | msdosfs_access, | |
1679 | msdosfs_getattr, | |
1680 | msdosfs_setattr, | |
1681 | msdosfs_read, | |
1682 | msdosfs_write, | |
1683 | msdosfs_ioctl, | |
1684 | msdosfs_select, | |
1685 | msdosfs_mmap, | |
1686 | msdosfs_fsync, | |
1687 | msdosfs_seek, | |
1688 | msdosfs_remove, | |
1689 | msdosfs_link, | |
1690 | msdosfs_rename, | |
1691 | msdosfs_mkdir, | |
1692 | msdosfs_rmdir, | |
1693 | msdosfs_symlink, | |
1694 | msdosfs_readdir, | |
1695 | msdosfs_readlink, | |
1696 | msdosfs_abortop, | |
1697 | msdosfs_inactive, | |
1698 | msdosfs_reclaim, | |
1699 | msdosfs_lock, | |
1700 | msdosfs_unlock, | |
1701 | msdosfs_bmap, | |
1702 | msdosfs_strategy, | |
1703 | msdosfs_print, | |
1704 | msdosfs_islocked, | |
1705 | msdosfs_advlock, | |
1706 | }; |