Line data Source code
1 : /* $OpenBSD: msdosfs_vnops.c,v 1.122 2018/06/21 14:17:23 visa Exp $ */
2 : /* $NetBSD: msdosfs_vnops.c,v 1.63 1997/10/17 11:24:19 ws Exp $ */
3 :
4 : /*-
5 : * Copyright (C) 2005 Thomas Wang.
6 : * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
7 : * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
8 : * All rights reserved.
9 : * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
10 : *
11 : * Redistribution and use in source and binary forms, with or without
12 : * modification, are permitted provided that the following conditions
13 : * are met:
14 : * 1. Redistributions of source code must retain the above copyright
15 : * notice, this list of conditions and the following disclaimer.
16 : * 2. Redistributions in binary form must reproduce the above copyright
17 : * notice, this list of conditions and the following disclaimer in the
18 : * documentation and/or other materials provided with the distribution.
19 : * 3. All advertising materials mentioning features or use of this software
20 : * must display the following acknowledgement:
21 : * This product includes software developed by TooLs GmbH.
22 : * 4. The name of TooLs GmbH may not be used to endorse or promote products
23 : * derived from this software without specific prior written permission.
24 : *
25 : * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
26 : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28 : * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 : * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30 : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
31 : * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
32 : * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
33 : * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
34 : * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 : */
36 : /*
37 : * Written by Paul Popelka (paulp@uts.amdahl.com)
38 : *
39 : * You can do anything you want with this software, just don't say you wrote
40 : * it, and don't remove this notice.
41 : *
42 : * This software is provided "as is".
43 : *
44 : * The author supplies this software to be publicly redistributed on the
45 : * understanding that the author is not responsible for the correct
46 : * functioning of this software in any circumstances and is not liable for
47 : * any damages caused by this software.
48 : *
49 : * October 1992
50 : */
51 :
52 : #include <sys/param.h>
53 : #include <sys/systm.h>
54 : #include <sys/namei.h>
55 : #include <sys/resourcevar.h> /* defines plimit structure in proc struct */
56 : #include <sys/fcntl.h> /* define FWRITE ... */
57 : #include <sys/file.h>
58 : #include <sys/stat.h>
59 : #include <sys/buf.h>
60 : #include <sys/proc.h>
61 : #include <sys/mount.h>
62 : #include <sys/vnode.h>
63 : #include <sys/lock.h>
64 : #include <sys/signalvar.h>
65 : #include <sys/specdev.h> /* XXX */ /* defines v_rdev */
66 : #include <sys/malloc.h>
67 : #include <sys/pool.h>
68 : #include <sys/dirent.h> /* defines dirent structure */
69 : #include <sys/lockf.h>
70 : #include <sys/poll.h>
71 : #include <sys/unistd.h>
72 :
73 : #include <msdosfs/bpb.h>
74 : #include <msdosfs/direntry.h>
75 : #include <msdosfs/denode.h>
76 : #include <msdosfs/msdosfsmount.h>
77 : #include <msdosfs/fat.h>
78 :
79 : static uint32_t fileidhash(uint64_t);
80 :
81 : int msdosfs_bmaparray(struct vnode *, uint32_t, daddr_t *, int *);
82 : int msdosfs_kqfilter(void *);
83 : int filt_msdosfsread(struct knote *, long);
84 : int filt_msdosfswrite(struct knote *, long);
85 : int filt_msdosfsvnode(struct knote *, long);
86 : void filt_msdosfsdetach(struct knote *);
87 :
88 : /*
89 : * Some general notes:
90 : *
91 : * In the ufs filesystem the inodes, superblocks, and indirect blocks are
92 : * read/written using the vnode for the filesystem. Blocks that represent
93 : * the contents of a file are read/written using the vnode for the file
94 : * (including directories when they are read/written as files). This
95 : * presents problems for the dos filesystem because data that should be in
96 : * an inode (if dos had them) resides in the directory itself. Since we
97 : * must update directory entries without the benefit of having the vnode
98 : * for the directory we must use the vnode for the filesystem. This means
99 : * that when a directory is actually read/written (via read, write, or
100 : * readdir, or seek) we must use the vnode for the filesystem instead of
101 : * the vnode for the directory as would happen in ufs. This is to insure we
102 : * retrieve the correct block from the buffer cache since the hash value is
103 : * based upon the vnode address and the desired block number.
104 : */
105 :
106 : /*
107 : * Create a regular file. On entry the directory to contain the file being
108 : * created is locked. We must release before we return. We must also free
109 : * the pathname buffer pointed at by cnp->cn_pnbuf, always on error, or
110 : * only if the SAVESTART bit in cn_flags is clear on success.
111 : */
112 : int
113 0 : msdosfs_create(void *v)
114 : {
115 0 : struct vop_create_args *ap = v;
116 0 : struct componentname *cnp = ap->a_cnp;
117 0 : struct denode ndirent;
118 0 : struct denode *dep;
119 0 : struct denode *pdep = VTODE(ap->a_dvp);
120 : int error;
121 0 : struct timespec ts;
122 :
123 : #ifdef MSDOSFS_DEBUG
124 : printf("msdosfs_create(cnp %p, vap %p\n", cnp, ap->a_vap);
125 : #endif
126 :
127 : /*
128 : * If this is the root directory and there is no space left we
129 : * can't do anything. This is because the root directory can not
130 : * change size.
131 : */
132 0 : if (pdep->de_StartCluster == MSDOSFSROOT
133 0 : && pdep->de_fndoffset >= pdep->de_FileSize) {
134 : error = ENOSPC;
135 0 : goto bad;
136 : }
137 :
138 : /*
139 : * Create a directory entry for the file, then call createde() to
140 : * have it installed. NOTE: DOS files are always executable. We
141 : * use the absence of the owner write bit to make the file
142 : * readonly.
143 : */
144 : #ifdef DIAGNOSTIC
145 0 : if ((cnp->cn_flags & HASBUF) == 0)
146 0 : panic("msdosfs_create: no name");
147 : #endif
148 0 : bzero(&ndirent, sizeof(ndirent));
149 0 : if ((error = uniqdosname(pdep, cnp, ndirent.de_Name)) != 0)
150 : goto bad;
151 :
152 0 : ndirent.de_Attributes = (ap->a_vap->va_mode & VWRITE) ?
153 : ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY;
154 0 : ndirent.de_StartCluster = 0;
155 0 : ndirent.de_FileSize = 0;
156 0 : ndirent.de_dev = pdep->de_dev;
157 0 : ndirent.de_devvp = pdep->de_devvp;
158 0 : ndirent.de_pmp = pdep->de_pmp;
159 0 : ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
160 0 : getnanotime(&ts);
161 0 : DETIMES(&ndirent, &ts, &ts, &ts);
162 0 : if ((error = createde(&ndirent, pdep, &dep, cnp)) != 0)
163 : goto bad;
164 0 : if ((cnp->cn_flags & SAVESTART) == 0)
165 0 : pool_put(&namei_pool, cnp->cn_pnbuf);
166 0 : VN_KNOTE(ap->a_dvp, NOTE_WRITE);
167 0 : *ap->a_vpp = DETOV(dep);
168 0 : return (0);
169 :
170 : bad:
171 0 : pool_put(&namei_pool, cnp->cn_pnbuf);
172 0 : return (error);
173 0 : }
174 :
175 : int
176 0 : msdosfs_mknod(void *v)
177 : {
178 0 : struct vop_mknod_args *ap = v;
179 :
180 0 : pool_put(&namei_pool, ap->a_cnp->cn_pnbuf);
181 0 : VN_KNOTE(ap->a_dvp, NOTE_WRITE);
182 0 : return (EINVAL);
183 : }
184 :
185 : int
186 0 : msdosfs_open(void *v)
187 : {
188 : #if 0
189 : struct vop_open_args /* {
190 : struct vnode *a_vp;
191 : int a_mode;
192 : struct ucred *a_cred;
193 : struct proc *a_p;
194 : } */ *ap;
195 : #endif
196 :
197 0 : return (0);
198 : }
199 :
200 : int
201 0 : msdosfs_close(void *v)
202 : {
203 0 : struct vop_close_args *ap = v;
204 0 : struct vnode *vp = ap->a_vp;
205 0 : struct denode *dep = VTODE(vp);
206 0 : struct timespec ts;
207 :
208 0 : if (vp->v_usecount > 1 && !VOP_ISLOCKED(vp)) {
209 0 : getnanotime(&ts);
210 0 : DETIMES(dep, &ts, &ts, &ts);
211 : }
212 0 : return (0);
213 0 : }
214 :
215 : int
216 0 : msdosfs_access(void *v)
217 : {
218 0 : struct vop_access_args *ap = v;
219 0 : struct denode *dep = VTODE(ap->a_vp);
220 0 : struct msdosfsmount *pmp = dep->de_pmp;
221 : mode_t dosmode;
222 :
223 : dosmode = (S_IRUSR|S_IRGRP|S_IROTH);
224 0 : if ((dep->de_Attributes & ATTR_READONLY) == 0)
225 0 : dosmode |= (S_IWUSR|S_IWGRP|S_IWOTH);
226 0 : if (dep->de_Attributes & ATTR_DIRECTORY) {
227 0 : dosmode |= (dosmode & S_IRUSR) ? S_IXUSR : 0;
228 0 : dosmode |= (dosmode & S_IRGRP) ? S_IXGRP : 0;
229 0 : dosmode |= (dosmode & S_IROTH) ? S_IXOTH : 0;
230 0 : }
231 0 : dosmode &= pmp->pm_mask;
232 :
233 0 : return (vaccess(ap->a_vp->v_type, dosmode, pmp->pm_uid, pmp->pm_gid,
234 0 : ap->a_mode, ap->a_cred));
235 : }
236 :
237 : int
238 0 : msdosfs_getattr(void *v)
239 : {
240 0 : struct vop_getattr_args *ap = v;
241 0 : struct denode *dep = VTODE(ap->a_vp);
242 0 : struct msdosfsmount *pmp = dep->de_pmp;
243 0 : struct vattr *vap = ap->a_vap;
244 0 : struct timespec ts;
245 : uint32_t fileid;
246 :
247 0 : getnanotime(&ts);
248 0 : DETIMES(dep, &ts, &ts, &ts);
249 0 : vap->va_fsid = dep->de_dev;
250 :
251 : /*
252 : * The following computation of the fileid must be the same as
253 : * that used in msdosfs_readdir() to compute d_fileno. If not,
254 : * pwd doesn't work.
255 : *
256 : * We now use the starting cluster number as the fileid/fileno.
257 : * This works for both files and directories (including the root
258 : * directory, on FAT32). Even on FAT32, this will at most be a
259 : * 28-bit number, as the high 4 bits of FAT32 cluster numbers
260 : * are reserved.
261 : *
262 : * However, we do need to do something for 0-length files, which
263 : * will not have a starting cluster number.
264 : *
265 : * These files cannot be directories, since (except for /, which
266 : * is special-cased anyway) directories contain entries for . and
267 : * .., so must have non-zero length.
268 : *
269 : * In this case, we just create a non-cryptographic hash of the
270 : * original fileid calculation, and set the top bit.
271 : *
272 : * This algorithm has the benefit that all directories, and all
273 : * non-zero-length files, will have fileids that are persistent
274 : * across mounts and reboots, and that cannot collide (as long
275 : * as the filesystem is not corrupt). Zero-length files will
276 : * have fileids that are persistent, but that may collide. We
277 : * will just have to live with that.
278 : */
279 0 : fileid = dep->de_StartCluster;
280 :
281 0 : if (dep->de_Attributes & ATTR_DIRECTORY) {
282 : /* Special-case root */
283 0 : if (dep->de_StartCluster == MSDOSFSROOT)
284 0 : fileid = FAT32(pmp) ? pmp->pm_rootdirblk : 1;
285 : } else {
286 0 : if (dep->de_FileSize == 0) {
287 : uint32_t dirsperblk;
288 : uint64_t fileid64;
289 :
290 0 : dirsperblk = pmp->pm_BytesPerSec /
291 : sizeof(struct direntry);
292 :
293 0 : fileid64 = (dep->de_dirclust == MSDOSFSROOT) ?
294 0 : roottobn(pmp, 0) : cntobn(pmp, dep->de_dirclust);
295 0 : fileid64 *= dirsperblk;
296 0 : fileid64 += dep->de_diroffset / sizeof(struct direntry);
297 :
298 0 : fileid = fileidhash(fileid64);
299 0 : }
300 : }
301 :
302 0 : vap->va_fileid = fileid;
303 0 : vap->va_mode = (S_IRUSR|S_IRGRP|S_IROTH);
304 0 : if ((dep->de_Attributes & ATTR_READONLY) == 0)
305 0 : vap->va_mode |= (S_IWUSR|S_IWGRP|S_IWOTH);
306 0 : if (dep->de_Attributes & ATTR_DIRECTORY) {
307 0 : vap->va_mode |= S_IFDIR;
308 0 : vap->va_mode |= (vap->va_mode & S_IRUSR) ? S_IXUSR : 0;
309 0 : vap->va_mode |= (vap->va_mode & S_IRGRP) ? S_IXGRP : 0;
310 0 : vap->va_mode |= (vap->va_mode & S_IROTH) ? S_IXOTH : 0;
311 0 : }
312 0 : vap->va_mode &= dep->de_pmp->pm_mask;
313 0 : vap->va_nlink = 1;
314 0 : vap->va_gid = dep->de_pmp->pm_gid;
315 0 : vap->va_uid = dep->de_pmp->pm_uid;
316 0 : vap->va_rdev = 0;
317 0 : vap->va_size = dep->de_FileSize;
318 0 : dos2unixtime(dep->de_MDate, dep->de_MTime, 0, &vap->va_mtime);
319 0 : if (dep->de_pmp->pm_flags & MSDOSFSMNT_LONGNAME) {
320 0 : dos2unixtime(dep->de_ADate, 0, 0, &vap->va_atime);
321 0 : dos2unixtime(dep->de_CDate, dep->de_CTime, dep->de_CTimeHundredth, &vap->va_ctime);
322 0 : } else {
323 0 : vap->va_atime = vap->va_mtime;
324 0 : vap->va_ctime = vap->va_mtime;
325 : }
326 0 : vap->va_flags = 0;
327 0 : if ((dep->de_Attributes & ATTR_ARCHIVE) == 0)
328 0 : vap->va_flags |= SF_ARCHIVED;
329 0 : vap->va_gen = 0;
330 0 : vap->va_blocksize = dep->de_pmp->pm_bpcluster;
331 0 : vap->va_bytes = (dep->de_FileSize + dep->de_pmp->pm_crbomask) &
332 0 : ~(dep->de_pmp->pm_crbomask);
333 0 : vap->va_type = ap->a_vp->v_type;
334 0 : return (0);
335 0 : }
336 :
337 : int
338 0 : msdosfs_setattr(void *v)
339 : {
340 0 : struct vop_setattr_args *ap = v;
341 0 : struct vnode *vp = ap->a_vp;
342 0 : struct denode *dep = VTODE(ap->a_vp);
343 0 : struct msdosfsmount *pmp = dep->de_pmp;
344 0 : struct vattr *vap = ap->a_vap;
345 0 : struct ucred *cred = ap->a_cred;
346 : int error = 0;
347 :
348 : #ifdef MSDOSFS_DEBUG
349 : printf("msdosfs_setattr(): vp %p, vap %p, cred %p, p %p\n",
350 : ap->a_vp, vap, cred, ap->a_p);
351 : #endif
352 0 : if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
353 0 : (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
354 0 : (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
355 0 : (vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
356 : #ifdef MSDOSFS_DEBUG
357 : printf("msdosfs_setattr(): returning EINVAL\n");
358 : printf(" va_type %d, va_nlink %x, va_fsid %ld, "
359 : "va_fileid %llx\n", vap->va_type, vap->va_nlink,
360 : vap->va_fsid, (unsigned long long)vap->va_fileid);
361 : printf(" va_blocksize %lx, va_rdev %x, va_bytes %llx, "
362 : "va_gen %lx\n", vap->va_blocksize,
363 : (unsigned int)vap->va_rdev,
364 : (unsigned long long)vap->va_bytes, vap->va_gen);
365 : #endif
366 0 : return (EINVAL);
367 : }
368 0 : if (vap->va_flags != VNOVAL) {
369 0 : if (vp->v_mount->mnt_flag & MNT_RDONLY)
370 0 : return (EINVAL);
371 0 : if (cred->cr_uid != pmp->pm_uid) {
372 0 : error = suser_ucred(cred);
373 0 : if (error)
374 0 : return (error);
375 : }
376 : /*
377 : * We are very inconsistent about handling unsupported
378 : * attributes. We ignored the access time and the
379 : * read and execute bits. We were strict for the other
380 : * attributes.
381 : *
382 : * Here we are strict, stricter than ufs in not allowing
383 : * users to attempt to set SF_SETTABLE bits or anyone to
384 : * set unsupported bits. However, we ignore attempts to
385 : * set ATTR_ARCHIVE for directories `cp -pr' from a more
386 : * sensible filesystem attempts it a lot.
387 : */
388 0 : if (vap->va_flags & SF_SETTABLE) {
389 0 : error = suser_ucred(cred);
390 0 : if (error)
391 0 : return (error);
392 : }
393 0 : if (vap->va_flags & ~SF_ARCHIVED)
394 0 : return EOPNOTSUPP;
395 0 : if (vap->va_flags & SF_ARCHIVED)
396 0 : dep->de_Attributes &= ~ATTR_ARCHIVE;
397 0 : else if (!(dep->de_Attributes & ATTR_DIRECTORY))
398 0 : dep->de_Attributes |= ATTR_ARCHIVE;
399 0 : dep->de_flag |= DE_MODIFIED;
400 0 : }
401 :
402 0 : if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
403 : uid_t uid;
404 : gid_t gid;
405 :
406 0 : if (vp->v_mount->mnt_flag & MNT_RDONLY)
407 0 : return (EINVAL);
408 0 : uid = vap->va_uid;
409 0 : if (uid == (uid_t)VNOVAL)
410 0 : uid = pmp->pm_uid;
411 0 : gid = vap->va_gid;
412 0 : if (gid == (gid_t)VNOVAL)
413 0 : gid = pmp->pm_gid;
414 0 : if (cred->cr_uid != pmp->pm_uid || uid != pmp->pm_uid ||
415 0 : (gid != pmp->pm_gid && !groupmember(gid, cred))) {
416 0 : error = suser_ucred(cred);
417 0 : if (error)
418 0 : return (error);
419 : }
420 0 : if (uid != pmp->pm_uid || gid != pmp->pm_gid)
421 0 : return EINVAL;
422 0 : }
423 :
424 0 : if (vap->va_size != VNOVAL) {
425 0 : switch (vp->v_type) {
426 : case VDIR:
427 0 : return (EISDIR);
428 : case VREG:
429 : /*
430 : * Truncation is only supported for regular files,
431 : * Disallow it if the filesystem is read-only.
432 : */
433 0 : if (vp->v_mount->mnt_flag & MNT_RDONLY)
434 0 : return (EINVAL);
435 : break;
436 : default:
437 : /*
438 : * According to POSIX, the result is unspecified
439 : * for file types other than regular files,
440 : * directories and shared memory objects. We
441 : * don't support any file types except regular
442 : * files and directories in this file system, so
443 : * this (default) case is unreachable and can do
444 : * anything. Keep falling through to detrunc()
445 : * for now.
446 : */
447 : break;
448 : }
449 0 : error = detrunc(dep, vap->va_size, 0, cred, ap->a_p);
450 0 : if (error)
451 0 : return error;
452 : }
453 0 : if ((vap->va_vaflags & VA_UTIMES_CHANGE) ||
454 0 : vap->va_atime.tv_nsec != VNOVAL ||
455 0 : vap->va_mtime.tv_nsec != VNOVAL) {
456 0 : if (vp->v_mount->mnt_flag & MNT_RDONLY)
457 0 : return (EINVAL);
458 0 : if (cred->cr_uid != pmp->pm_uid &&
459 0 : (error = suser_ucred(cred)) &&
460 0 : ((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
461 0 : (error = VOP_ACCESS(ap->a_vp, VWRITE, cred, ap->a_p))))
462 0 : return (error);
463 0 : if (vp->v_type != VDIR) {
464 0 : if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95) == 0 &&
465 0 : vap->va_atime.tv_nsec != VNOVAL) {
466 0 : dep->de_flag &= ~DE_ACCESS;
467 0 : unix2dostime(&vap->va_atime, &dep->de_ADate,
468 : NULL, NULL);
469 0 : }
470 0 : if (vap->va_mtime.tv_nsec != VNOVAL) {
471 0 : dep->de_flag &= ~DE_UPDATE;
472 0 : unix2dostime(&vap->va_mtime, &dep->de_MDate,
473 0 : &dep->de_MTime, NULL);
474 0 : }
475 0 : dep->de_Attributes |= ATTR_ARCHIVE;
476 0 : dep->de_flag |= DE_MODIFIED;
477 0 : }
478 : }
479 : /*
480 : * DOS files only have the ability to have their writability
481 : * attribute set, so we use the owner write bit to set the readonly
482 : * attribute.
483 : */
484 0 : if (vap->va_mode != (mode_t)VNOVAL) {
485 0 : if (vp->v_mount->mnt_flag & MNT_RDONLY)
486 0 : return (EINVAL);
487 0 : if (cred->cr_uid != pmp->pm_uid) {
488 0 : error = suser_ucred(cred);
489 0 : if (error)
490 0 : return (error);
491 : }
492 0 : if (vp->v_type != VDIR) {
493 : /* We ignore the read and execute bits. */
494 0 : if (vap->va_mode & VWRITE)
495 0 : dep->de_Attributes &= ~ATTR_READONLY;
496 : else
497 0 : dep->de_Attributes |= ATTR_READONLY;
498 0 : dep->de_Attributes |= ATTR_ARCHIVE;
499 0 : dep->de_flag |= DE_MODIFIED;
500 0 : }
501 : }
502 0 : VN_KNOTE(ap->a_vp, NOTE_ATTRIB);
503 0 : return (deupdat(dep, 1));
504 0 : }
505 :
506 : int
507 0 : msdosfs_read(void *v)
508 : {
509 0 : struct vop_read_args *ap = v;
510 0 : struct vnode *vp = ap->a_vp;
511 0 : struct denode *dep = VTODE(vp);
512 0 : struct msdosfsmount *pmp = dep->de_pmp;
513 0 : struct uio *uio = ap->a_uio;
514 : int isadir, error = 0;
515 0 : uint32_t n, diff, size, on;
516 0 : struct buf *bp;
517 : uint32_t cn;
518 0 : daddr_t bn;
519 :
520 : /*
521 : * If they didn't ask for any data, then we are done.
522 : */
523 0 : if (uio->uio_resid == 0)
524 0 : return (0);
525 0 : if (uio->uio_offset < 0)
526 0 : return (EINVAL);
527 :
528 0 : isadir = dep->de_Attributes & ATTR_DIRECTORY;
529 0 : do {
530 0 : if (uio->uio_offset >= dep->de_FileSize)
531 0 : return (0);
532 :
533 0 : cn = de_cluster(pmp, uio->uio_offset);
534 0 : size = pmp->pm_bpcluster;
535 0 : on = uio->uio_offset & pmp->pm_crbomask;
536 0 : n = ulmin(pmp->pm_bpcluster - on, uio->uio_resid);
537 :
538 : /*
539 : * de_FileSize is uint32_t, and we know that uio_offset <
540 : * de_FileSize, so uio->uio_offset < 2^32. Therefore
541 : * the cast to uint32_t on the next line is safe.
542 : */
543 0 : diff = dep->de_FileSize - (uint32_t)uio->uio_offset;
544 0 : if (diff < n)
545 0 : n = diff;
546 :
547 : /*
548 : * If we are operating on a directory file then be sure to
549 : * do i/o with the vnode for the filesystem instead of the
550 : * vnode for the directory.
551 : */
552 0 : if (isadir) {
553 : /* convert cluster # to block # */
554 0 : error = pcbmap(dep, cn, &bn, 0, &size);
555 0 : if (error)
556 0 : return (error);
557 0 : error = bread(pmp->pm_devvp, bn, size, &bp);
558 0 : } else {
559 0 : if (de_cn2off(pmp, cn + 1) >= dep->de_FileSize)
560 0 : error = bread(vp, cn, size, &bp);
561 : else
562 0 : error = bread_cluster(vp, cn, size, &bp);
563 : }
564 0 : n = min(n, pmp->pm_bpcluster - bp->b_resid);
565 0 : if (error) {
566 0 : brelse(bp);
567 0 : return (error);
568 : }
569 0 : error = uiomove(bp->b_data + on, n, uio);
570 0 : brelse(bp);
571 0 : } while (error == 0 && uio->uio_resid > 0 && n != 0);
572 0 : if (!isadir && !(vp->v_mount->mnt_flag & MNT_NOATIME))
573 0 : dep->de_flag |= DE_ACCESS;
574 0 : return (error);
575 0 : }
576 :
577 : /*
578 : * Write data to a file or directory.
579 : */
580 : int
581 0 : msdosfs_write(void *v)
582 : {
583 0 : struct vop_write_args *ap = v;
584 : uint32_t n, croffset;
585 : size_t resid;
586 0 : ssize_t overrun;
587 : int extended = 0;
588 : uint32_t osize;
589 : int error = 0;
590 : uint32_t count, lastcn, cn;
591 0 : struct buf *bp;
592 0 : int ioflag = ap->a_ioflag;
593 0 : struct uio *uio = ap->a_uio;
594 0 : struct vnode *vp = ap->a_vp;
595 : struct vnode *thisvp;
596 0 : struct denode *dep = VTODE(vp);
597 0 : struct msdosfsmount *pmp = dep->de_pmp;
598 0 : struct ucred *cred = ap->a_cred;
599 :
600 : #ifdef MSDOSFS_DEBUG
601 : printf("msdosfs_write(vp %p, uio %p, ioflag %08x, cred %p\n",
602 : vp, uio, ioflag, cred);
603 : printf("msdosfs_write(): diroff %d, dirclust %d, startcluster %d\n",
604 : dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster);
605 : #endif
606 :
607 0 : switch (vp->v_type) {
608 : case VREG:
609 0 : if (ioflag & IO_APPEND)
610 0 : uio->uio_offset = dep->de_FileSize;
611 : thisvp = vp;
612 : break;
613 : case VDIR:
614 0 : return EISDIR;
615 : default:
616 0 : panic("msdosfs_write(): bad file type");
617 : }
618 :
619 0 : if (uio->uio_offset < 0)
620 0 : return (EINVAL);
621 :
622 0 : if (uio->uio_resid == 0)
623 0 : return (0);
624 :
625 : /* Don't bother to try to write files larger than the f/s limit */
626 0 : if (uio->uio_offset > MSDOSFS_FILESIZE_MAX ||
627 0 : uio->uio_resid > (MSDOSFS_FILESIZE_MAX - uio->uio_offset))
628 0 : return (EFBIG);
629 :
630 : /* do the filesize rlimit check */
631 0 : if ((error = vn_fsizechk(vp, uio, ioflag, &overrun)))
632 0 : return (error);
633 :
634 : /*
635 : * If the offset we are starting the write at is beyond the end of
636 : * the file, then they've done a seek. Unix filesystems allow
637 : * files with holes in them, DOS doesn't so we must fill the hole
638 : * with zeroed blocks.
639 : */
640 0 : if (uio->uio_offset > dep->de_FileSize) {
641 0 : if ((error = deextend(dep, uio->uio_offset, cred)) != 0)
642 : goto out;
643 : }
644 :
645 : /*
646 : * Remember some values in case the write fails.
647 : */
648 0 : resid = uio->uio_resid;
649 0 : osize = dep->de_FileSize;
650 :
651 : /*
652 : * If we write beyond the end of the file, extend it to its ultimate
653 : * size ahead of the time to hopefully get a contiguous area.
654 : */
655 0 : if (uio->uio_offset + resid > osize) {
656 : extended = 1;
657 0 : count = de_clcount(pmp, uio->uio_offset + resid) -
658 0 : de_clcount(pmp, osize);
659 0 : if ((error = extendfile(dep, count, NULL, NULL, 0)) &&
660 0 : (error != ENOSPC || (ioflag & IO_UNIT)))
661 : goto errexit;
662 0 : lastcn = dep->de_fc[FC_LASTFC].fc_frcn;
663 0 : } else
664 0 : lastcn = de_clcount(pmp, osize) - 1;
665 :
666 0 : do {
667 0 : croffset = uio->uio_offset & pmp->pm_crbomask;
668 0 : cn = de_cluster(pmp, uio->uio_offset);
669 :
670 0 : if (cn > lastcn) {
671 : error = ENOSPC;
672 0 : break;
673 : }
674 :
675 0 : if (croffset == 0 &&
676 0 : (de_cluster(pmp, uio->uio_offset + uio->uio_resid) > cn ||
677 0 : (uio->uio_offset + uio->uio_resid) >= dep->de_FileSize)) {
678 : /*
679 : * If either the whole cluster gets written,
680 : * or we write the cluster from its start beyond EOF,
681 : * then no need to read data from disk.
682 : */
683 0 : bp = getblk(thisvp, cn, pmp->pm_bpcluster, 0, 0);
684 0 : clrbuf(bp);
685 : /*
686 : * Do the bmap now, since pcbmap needs buffers
687 : * for the fat table. (see msdosfs_strategy)
688 : */
689 0 : if (bp->b_blkno == bp->b_lblkno) {
690 0 : error = pcbmap(dep, bp->b_lblkno, &bp->b_blkno, 0, 0);
691 0 : if (error)
692 0 : bp->b_blkno = -1;
693 : }
694 0 : if (bp->b_blkno == -1) {
695 0 : brelse(bp);
696 0 : if (!error)
697 0 : error = EIO; /* XXX */
698 : break;
699 : }
700 : } else {
701 : /*
702 : * The block we need to write into exists, so
703 : * read it in.
704 : */
705 0 : error = bread(thisvp, cn, pmp->pm_bpcluster, &bp);
706 0 : if (error) {
707 0 : brelse(bp);
708 0 : break;
709 : }
710 : }
711 :
712 0 : n = ulmin(uio->uio_resid, pmp->pm_bpcluster - croffset);
713 0 : if (uio->uio_offset + n > dep->de_FileSize) {
714 0 : dep->de_FileSize = uio->uio_offset + n;
715 0 : uvm_vnp_setsize(vp, dep->de_FileSize);
716 0 : }
717 0 : uvm_vnp_uncache(vp);
718 : /*
719 : * Should these vnode_pager_* functions be done on dir
720 : * files?
721 : */
722 :
723 : /*
724 : * Copy the data from user space into the buf header.
725 : */
726 0 : error = uiomove(bp->b_data + croffset, n, uio);
727 :
728 : /*
729 : * If they want this synchronous then write it and wait for
730 : * it. Otherwise, if on a cluster boundary write it
731 : * asynchronously so we can move on to the next block
732 : * without delay. Otherwise do a delayed write because we
733 : * may want to write somemore into the block later.
734 : */
735 : #if 0
736 : if (ioflag & IO_NOCACHE)
737 : bp->b_flags |= B_NOCACHE;
738 : #endif
739 0 : if (ioflag & IO_SYNC)
740 0 : (void) bwrite(bp);
741 0 : else if (n + croffset == pmp->pm_bpcluster)
742 0 : bawrite(bp);
743 : else
744 0 : bdwrite(bp);
745 0 : dep->de_flag |= DE_UPDATE;
746 0 : } while (error == 0 && uio->uio_resid > 0);
747 :
748 0 : if (resid > uio->uio_resid)
749 0 : VN_KNOTE(ap->a_vp, NOTE_WRITE | (extended ? NOTE_EXTEND : 0));
750 :
751 0 : if (dep->de_FileSize < osize)
752 0 : VN_KNOTE(ap->a_vp, NOTE_TRUNCATE);
753 :
754 : /*
755 : * If the write failed and they want us to, truncate the file back
756 : * to the size it was before the write was attempted.
757 : */
758 : errexit:
759 0 : if (error) {
760 0 : if (ioflag & IO_UNIT) {
761 0 : detrunc(dep, osize, ioflag & IO_SYNC, NOCRED, NULL);
762 0 : uio->uio_offset -= resid - uio->uio_resid;
763 0 : uio->uio_resid = resid;
764 0 : } else {
765 0 : detrunc(dep, dep->de_FileSize, ioflag & IO_SYNC, NOCRED, NULL);
766 0 : if (uio->uio_resid != resid)
767 0 : error = 0;
768 : }
769 0 : } else if (ioflag & IO_SYNC)
770 0 : error = deupdat(dep, 1);
771 :
772 : out:
773 : /* correct the result for writes clamped by vn_fsizechk() */
774 0 : uio->uio_resid += overrun;
775 0 : return (error);
776 0 : }
777 :
778 : int
779 0 : msdosfs_ioctl(void *v)
780 : {
781 : #if 0
782 : struct vop_ioctl_args /* {
783 : struct vnode *a_vp;
784 : uint32_t a_command;
785 : caddr_t a_data;
786 : int a_fflag;
787 : struct ucred *a_cred;
788 : struct proc *a_p;
789 : } */ *ap;
790 : #endif
791 :
792 0 : return (ENOTTY);
793 : }
794 :
795 : int
796 0 : msdosfs_poll(void *v)
797 : {
798 0 : struct vop_poll_args *ap = v;
799 :
800 0 : return (ap->a_events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM));
801 : }
802 :
803 : /*
804 : * Flush the blocks of a file to disk.
805 : *
806 : * This function is worthless for vnodes that represent directories. Maybe we
807 : * could just do a sync if they try an fsync on a directory file.
808 : */
809 : int
810 0 : msdosfs_fsync(void *v)
811 : {
812 0 : struct vop_fsync_args *ap = v;
813 0 : struct vnode *vp = ap->a_vp;
814 :
815 0 : vflushbuf(vp, ap->a_waitfor == MNT_WAIT);
816 0 : return (deupdat(VTODE(vp), ap->a_waitfor == MNT_WAIT));
817 : }
818 :
819 : /*
820 : * Flush the blocks of a file to disk.
821 : *
822 : * This function is worthless for vnodes that represent directories. Maybe we
823 : * could just do a sync if they try an fsync on a directory file.
824 : */
825 : int
826 0 : msdosfs_remove(void *v)
827 : {
828 0 : struct vop_remove_args *ap = v;
829 0 : struct denode *dep = VTODE(ap->a_vp);
830 0 : struct denode *ddep = VTODE(ap->a_dvp);
831 : int error;
832 :
833 0 : if (ap->a_vp->v_type == VDIR)
834 0 : error = EPERM;
835 : else
836 0 : error = removede(ddep, dep);
837 :
838 0 : VN_KNOTE(ap->a_vp, NOTE_DELETE);
839 0 : VN_KNOTE(ap->a_dvp, NOTE_WRITE);
840 :
841 : #ifdef MSDOSFS_DEBUG
842 : printf("msdosfs_remove(), dep %p, v_usecount %d\n", dep,
843 : ap->a_vp->v_usecount);
844 : #endif
845 0 : if (ddep == dep)
846 0 : vrele(ap->a_vp);
847 : else
848 0 : vput(ap->a_vp); /* causes msdosfs_inactive() to be called
849 : * via vrele() */
850 0 : vput(ap->a_dvp);
851 0 : return (error);
852 : }
853 :
854 : /*
855 : * DOS filesystems don't know what links are. But since we already called
856 : * msdosfs_lookup() with create and lockparent, the parent is locked so we
857 : * have to free it before we return the error.
858 : */
859 : int
860 0 : msdosfs_link(void *v)
861 : {
862 0 : struct vop_link_args *ap = v;
863 :
864 0 : VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
865 0 : vput(ap->a_dvp);
866 0 : return (EOPNOTSUPP);
867 : }
868 :
869 : /*
870 : * Renames on files require moving the denode to a new hash queue since the
871 : * denode's location is used to compute which hash queue to put the file
872 : * in. Unless it is a rename in place. For example "mv a b".
873 : *
874 : * What follows is the basic algorithm:
875 : *
876 : * if (file move) {
877 : * if (dest file exists) {
878 : * remove dest file
879 : * }
880 : * if (dest and src in same directory) {
881 : * rewrite name in existing directory slot
882 : * } else {
883 : * write new entry in dest directory
884 : * update offset and dirclust in denode
885 : * move denode to new hash chain
886 : * clear old directory entry
887 : * }
888 : * } else {
889 : * directory move
890 : * if (dest directory exists) {
891 : * if (dest is not empty) {
892 : * return ENOTEMPTY
893 : * }
894 : * remove dest directory
895 : * }
896 : * if (dest and src in same directory) {
897 : * rewrite name in existing entry
898 : * } else {
899 : * be sure dest is not a child of src directory
900 : * write entry in dest directory
901 : * update "." and ".." in moved directory
902 : * update offset and dirclust in denode
903 : * move denode to new hash chain
904 : * clear old directory entry for moved directory
905 : * }
906 : * }
907 : *
908 : * On entry:
909 : * source's parent directory is unlocked
910 : * source file or directory is unlocked
911 : * destination's parent directory is locked
912 : * destination file or directory is locked if it exists
913 : *
914 : * On exit:
915 : * all denodes should be released
916 : *
917 : * Notes:
918 : * I'm not sure how the memory containing the pathnames pointed at by the
919 : * componentname structures is freed, there may be some memory bleeding
920 : * for each rename done.
921 : */
922 : int
923 0 : msdosfs_rename(void *v)
924 : {
925 0 : struct vop_rename_args *ap = v;
926 0 : struct vnode *tvp = ap->a_tvp;
927 0 : struct vnode *tdvp = ap->a_tdvp;
928 0 : struct vnode *fvp = ap->a_fvp;
929 0 : struct vnode *fdvp = ap->a_fdvp;
930 0 : struct componentname *tcnp = ap->a_tcnp;
931 0 : struct componentname *fcnp = ap->a_fcnp;
932 : struct denode *ip, *xp, *dp, *zp;
933 0 : u_char toname[11], oldname[11];
934 : uint32_t from_diroffset, to_diroffset;
935 : u_char to_count;
936 : int doingdirectory = 0, newparent = 0;
937 : int error;
938 : uint32_t cn, pcl;
939 : daddr_t bn;
940 : struct msdosfsmount *pmp;
941 : struct direntry *dotdotp;
942 0 : struct buf *bp;
943 :
944 0 : pmp = VFSTOMSDOSFS(fdvp->v_mount);
945 :
946 : #ifdef DIAGNOSTIC
947 0 : if ((tcnp->cn_flags & HASBUF) == 0 ||
948 0 : (fcnp->cn_flags & HASBUF) == 0)
949 0 : panic("msdosfs_rename: no name");
950 : #endif
951 : /*
952 : * Check for cross-device rename.
953 : */
954 0 : if ((fvp->v_mount != tdvp->v_mount) ||
955 0 : (tvp && (fvp->v_mount != tvp->v_mount))) {
956 0 : error = EXDEV;
957 : abortit:
958 0 : VOP_ABORTOP(tdvp, tcnp);
959 0 : if (tdvp == tvp)
960 0 : vrele(tdvp);
961 : else
962 0 : vput(tdvp);
963 0 : if (tvp)
964 0 : vput(tvp);
965 0 : VOP_ABORTOP(fdvp, fcnp);
966 0 : vrele(fdvp);
967 0 : vrele(fvp);
968 0 : return (error);
969 : }
970 :
971 : /*
972 : * If source and dest are the same, do nothing.
973 : */
974 0 : if (tvp == fvp) {
975 : error = 0;
976 0 : goto abortit;
977 : }
978 :
979 : /* */
980 0 : if ((error = vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY)) != 0)
981 : goto abortit;
982 0 : dp = VTODE(fdvp);
983 0 : ip = VTODE(fvp);
984 :
985 : /*
986 : * Be sure we are not renaming ".", "..", or an alias of ".". This
987 : * leads to a crippled directory tree. It's pretty tough to do a
988 : * "ls" or "pwd" with the "." directory entry missing, and "cd .."
989 : * doesn't work if the ".." entry is missing.
990 : */
991 0 : if (ip->de_Attributes & ATTR_DIRECTORY) {
992 : /*
993 : * Avoid ".", "..", and aliases of "." for obvious reasons.
994 : */
995 0 : if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
996 0 : dp == ip ||
997 0 : (fcnp->cn_flags & ISDOTDOT) ||
998 0 : (tcnp->cn_flags & ISDOTDOT) ||
999 0 : (ip->de_flag & DE_RENAME)) {
1000 0 : VOP_UNLOCK(fvp);
1001 : error = EINVAL;
1002 0 : goto abortit;
1003 : }
1004 0 : ip->de_flag |= DE_RENAME;
1005 : doingdirectory++;
1006 0 : }
1007 0 : VN_KNOTE(fdvp, NOTE_WRITE); /* XXX right place? */
1008 :
1009 : /*
1010 : * When the target exists, both the directory
1011 : * and target vnodes are returned locked.
1012 : */
1013 0 : dp = VTODE(tdvp);
1014 0 : xp = tvp ? VTODE(tvp) : NULL;
1015 : /*
1016 : * Remember direntry place to use for destination
1017 : */
1018 0 : to_diroffset = dp->de_fndoffset;
1019 0 : to_count = dp->de_fndcnt;
1020 :
1021 : /*
1022 : * If ".." must be changed (ie the directory gets a new
1023 : * parent) then the source directory must not be in the
1024 : * directory hierarchy above the target, as this would
1025 : * orphan everything below the source directory. Also
1026 : * the user must have write permission in the source so
1027 : * as to be able to change "..". We must repeat the call
1028 : * to namei, as the parent directory is unlocked by the
1029 : * call to doscheckpath().
1030 : */
1031 0 : error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
1032 0 : VOP_UNLOCK(fvp);
1033 0 : if (VTODE(fdvp)->de_StartCluster != VTODE(tdvp)->de_StartCluster)
1034 0 : newparent = 1;
1035 0 : vrele(fdvp);
1036 0 : if (doingdirectory && newparent) {
1037 0 : if (error) /* write access check above */
1038 : goto bad1;
1039 0 : if (xp != NULL)
1040 0 : vput(tvp);
1041 : /*
1042 : * doscheckpath() vput()'s dp,
1043 : * so we have to do a relookup afterwards
1044 : */
1045 0 : if ((error = doscheckpath(ip, dp)) != 0)
1046 : goto out;
1047 0 : if ((tcnp->cn_flags & SAVESTART) == 0)
1048 0 : panic("msdosfs_rename: lost to startdir");
1049 0 : if ((error = vfs_relookup(tdvp, &tvp, tcnp)) != 0)
1050 : goto out;
1051 0 : dp = VTODE(tdvp);
1052 0 : xp = tvp ? VTODE(tvp) : NULL;
1053 0 : }
1054 :
1055 0 : VN_KNOTE(tdvp, NOTE_WRITE);
1056 :
1057 0 : if (xp != NULL) {
1058 : /*
1059 : * Target must be empty if a directory and have no links
1060 : * to it. Also, ensure source and target are compatible
1061 : * (both directories, or both not directories).
1062 : */
1063 0 : if (xp->de_Attributes & ATTR_DIRECTORY) {
1064 0 : if (!dosdirempty(xp)) {
1065 : error = ENOTEMPTY;
1066 0 : goto bad1;
1067 : }
1068 0 : if (!doingdirectory) {
1069 : error = ENOTDIR;
1070 0 : goto bad1;
1071 : }
1072 0 : cache_purge(tdvp);
1073 0 : } else if (doingdirectory) {
1074 : error = EISDIR;
1075 0 : goto bad1;
1076 : }
1077 0 : if ((error = removede(dp, xp)) != 0)
1078 : goto bad1;
1079 0 : VN_KNOTE(tvp, NOTE_DELETE);
1080 0 : vput(tvp);
1081 : xp = NULL;
1082 0 : }
1083 :
1084 : /*
1085 : * Convert the filename in tcnp into a dos filename. We copy this
1086 : * into the denode and directory entry for the destination
1087 : * file/directory.
1088 : */
1089 0 : if ((error = uniqdosname(VTODE(tdvp), tcnp, toname)) != 0)
1090 : goto bad1;
1091 :
1092 : /*
1093 : * Since from wasn't locked at various places above,
1094 : * have to do a relookup here.
1095 : */
1096 0 : fcnp->cn_flags &= ~MODMASK;
1097 0 : fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
1098 0 : if ((fcnp->cn_flags & SAVESTART) == 0)
1099 0 : panic("msdosfs_rename: lost from startdir");
1100 0 : if (!newparent)
1101 0 : VOP_UNLOCK(tdvp);
1102 0 : (void) vfs_relookup(fdvp, &fvp, fcnp);
1103 0 : if (fvp == NULL) {
1104 : /*
1105 : * From name has disappeared.
1106 : */
1107 0 : if (doingdirectory)
1108 0 : panic("rename: lost dir entry");
1109 0 : vrele(ap->a_fvp);
1110 0 : if (newparent)
1111 0 : VOP_UNLOCK(tdvp);
1112 0 : vrele(tdvp);
1113 0 : return 0;
1114 : }
1115 0 : xp = VTODE(fvp);
1116 0 : zp = VTODE(fdvp);
1117 0 : from_diroffset = zp->de_fndoffset;
1118 :
1119 : /*
1120 : * Ensure that the directory entry still exists and has not
1121 : * changed till now. If the source is a file the entry may
1122 : * have been unlinked or renamed. In either case there is
1123 : * no further work to be done. If the source is a directory
1124 : * then it cannot have been rmdir'ed or renamed; this is
1125 : * prohibited by the DE_RENAME flag.
1126 : */
1127 0 : if (xp != ip) {
1128 0 : if (doingdirectory)
1129 0 : panic("rename: lost dir entry");
1130 0 : vrele(ap->a_fvp);
1131 0 : if (newparent)
1132 0 : VOP_UNLOCK(fdvp);
1133 : xp = NULL;
1134 0 : } else {
1135 0 : vrele(fvp);
1136 : xp = NULL;
1137 :
1138 : /*
1139 : * First write a new entry in the destination
1140 : * directory and mark the entry in the source directory
1141 : * as deleted. Then move the denode to the correct hash
1142 : * chain for its new location in the filesystem. And, if
1143 : * we moved a directory, then update its .. entry to point
1144 : * to the new parent directory.
1145 : */
1146 0 : bcopy(ip->de_Name, oldname, 11);
1147 0 : bcopy(toname, ip->de_Name, 11); /* update denode */
1148 0 : dp->de_fndoffset = to_diroffset;
1149 0 : dp->de_fndcnt = to_count;
1150 0 : error = createde(ip, dp, NULL, tcnp);
1151 0 : if (error) {
1152 0 : bcopy(oldname, ip->de_Name, 11);
1153 0 : if (newparent)
1154 0 : VOP_UNLOCK(fdvp);
1155 : goto bad;
1156 : }
1157 0 : ip->de_refcnt++;
1158 0 : zp->de_fndoffset = from_diroffset;
1159 0 : if ((error = removede(zp, ip)) != 0) {
1160 : /* XXX should really panic here, fs is corrupt */
1161 0 : if (newparent)
1162 0 : VOP_UNLOCK(fdvp);
1163 : goto bad;
1164 : }
1165 :
1166 0 : cache_purge(fvp);
1167 :
1168 0 : if (!doingdirectory) {
1169 0 : error = pcbmap(dp, de_cluster(pmp, to_diroffset), 0,
1170 0 : &ip->de_dirclust, 0);
1171 0 : if (error) {
1172 : /* XXX should really panic here, fs is corrupt */
1173 0 : if (newparent)
1174 0 : VOP_UNLOCK(fdvp);
1175 : goto bad;
1176 : }
1177 0 : ip->de_diroffset = to_diroffset;
1178 0 : if (ip->de_dirclust != MSDOSFSROOT)
1179 0 : ip->de_diroffset &= pmp->pm_crbomask;
1180 : }
1181 0 : reinsert(ip);
1182 0 : if (newparent)
1183 0 : VOP_UNLOCK(fdvp);
1184 : }
1185 :
1186 : /*
1187 : * If we moved a directory to a new parent directory, then we must
1188 : * fixup the ".." entry in the moved directory.
1189 : */
1190 0 : if (doingdirectory && newparent) {
1191 0 : cn = ip->de_StartCluster;
1192 0 : if (cn == MSDOSFSROOT) {
1193 : /* this should never happen */
1194 0 : panic("msdosfs_rename: updating .. in root directory?");
1195 : } else
1196 0 : bn = cntobn(pmp, cn);
1197 0 : error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, &bp);
1198 0 : if (error) {
1199 : /* XXX should really panic here, fs is corrupt */
1200 0 : brelse(bp);
1201 0 : goto bad;
1202 : }
1203 0 : dotdotp = (struct direntry *)bp->b_data;
1204 0 : putushort(dotdotp[0].deStartCluster, cn);
1205 0 : pcl = dp->de_StartCluster;
1206 0 : if (FAT32(pmp) && pcl == pmp->pm_rootdirblk)
1207 0 : pcl = 0;
1208 0 : putushort(dotdotp[1].deStartCluster, pcl);
1209 0 : if (FAT32(pmp)) {
1210 0 : putushort(dotdotp[0].deHighClust, cn >> 16);
1211 0 : putushort(dotdotp[1].deHighClust, pcl >> 16);
1212 0 : }
1213 0 : if ((error = bwrite(bp)) != 0) {
1214 : /* XXX should really panic here, fs is corrupt */
1215 : goto bad;
1216 : }
1217 : }
1218 :
1219 0 : VN_KNOTE(fvp, NOTE_RENAME);
1220 :
1221 : bad:
1222 0 : VOP_UNLOCK(fvp);
1223 0 : vrele(fdvp);
1224 : bad1:
1225 0 : if (xp)
1226 0 : vput(tvp);
1227 0 : vput(tdvp);
1228 : out:
1229 0 : ip->de_flag &= ~DE_RENAME;
1230 0 : vrele(fvp);
1231 0 : return (error);
1232 :
1233 0 : }
1234 :
1235 : struct {
1236 : struct direntry dot;
1237 : struct direntry dotdot;
1238 : } dosdirtemplate = {
1239 : { ". ", " ", /* the . entry */
1240 : ATTR_DIRECTORY, /* file attribute */
1241 : CASE_LOWER_BASE | CASE_LOWER_EXT, /* lower case */
1242 : 0, /* create time 100ths */
1243 : { 0, 0 }, { 0, 0 }, /* create time & date */
1244 : { 0, 0 }, /* access date */
1245 : { 0, 0 }, /* high bits of start cluster */
1246 : { 210, 4 }, { 210, 4 }, /* modify time & date */
1247 : { 0, 0 }, /* startcluster */
1248 : { 0, 0, 0, 0 } /* filesize */
1249 : },
1250 : { ".. ", " ", /* the .. entry */
1251 : ATTR_DIRECTORY, /* file attribute */
1252 : CASE_LOWER_BASE | CASE_LOWER_EXT, /* lower case */
1253 : 0, /* create time 100ths */
1254 : { 0, 0 }, { 0, 0 }, /* create time & date */
1255 : { 0, 0 }, /* access date */
1256 : { 0, 0 }, /* high bits of start cluster */
1257 : { 210, 4 }, { 210, 4 }, /* modify time & date */
1258 : { 0, 0 }, /* startcluster */
1259 : { 0, 0, 0, 0 } /* filesize */
1260 : }
1261 : };
1262 :
1263 : int
1264 0 : msdosfs_mkdir(void *v)
1265 : {
1266 0 : struct vop_mkdir_args *ap = v;
1267 0 : struct componentname *cnp = ap->a_cnp;
1268 0 : struct denode ndirent;
1269 0 : struct denode *dep;
1270 0 : struct denode *pdep = VTODE(ap->a_dvp);
1271 : int error;
1272 : daddr_t bn;
1273 0 : uint32_t newcluster, pcl;
1274 : struct direntry *denp;
1275 0 : struct msdosfsmount *pmp = pdep->de_pmp;
1276 : struct buf *bp;
1277 0 : struct timespec ts;
1278 :
1279 : /*
1280 : * If this is the root directory and there is no space left we
1281 : * can't do anything. This is because the root directory can not
1282 : * change size.
1283 : */
1284 0 : if (pdep->de_StartCluster == MSDOSFSROOT
1285 0 : && pdep->de_fndoffset >= pdep->de_FileSize) {
1286 : error = ENOSPC;
1287 0 : goto bad2;
1288 : }
1289 :
1290 : /*
1291 : * Allocate a cluster to hold the about to be created directory.
1292 : */
1293 0 : error = clusteralloc(pmp, 0, 1, &newcluster, NULL);
1294 0 : if (error)
1295 : goto bad2;
1296 :
1297 0 : bzero(&ndirent, sizeof(ndirent));
1298 0 : ndirent.de_pmp = pmp;
1299 0 : ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
1300 0 : getnanotime(&ts);
1301 0 : DETIMES(&ndirent, &ts, &ts, &ts);
1302 :
1303 : /*
1304 : * Now fill the cluster with the "." and ".." entries. And write
1305 : * the cluster to disk. This way it is there for the parent
1306 : * directory to be pointing at if there were a crash.
1307 : */
1308 0 : bn = cntobn(pmp, newcluster);
1309 : /* always succeeds */
1310 0 : bp = getblk(pmp->pm_devvp, bn, pmp->pm_bpcluster, 0, 0);
1311 0 : bzero(bp->b_data, pmp->pm_bpcluster);
1312 0 : bcopy(&dosdirtemplate, bp->b_data, sizeof dosdirtemplate);
1313 0 : denp = (struct direntry *)bp->b_data;
1314 0 : putushort(denp[0].deStartCluster, newcluster);
1315 0 : putushort(denp[0].deCDate, ndirent.de_CDate);
1316 0 : putushort(denp[0].deCTime, ndirent.de_CTime);
1317 0 : denp[0].deCTimeHundredth = ndirent.de_CTimeHundredth;
1318 0 : putushort(denp[0].deADate, ndirent.de_ADate);
1319 0 : putushort(denp[0].deMDate, ndirent.de_MDate);
1320 0 : putushort(denp[0].deMTime, ndirent.de_MTime);
1321 0 : pcl = pdep->de_StartCluster;
1322 0 : if (FAT32(pmp) && pcl == pmp->pm_rootdirblk)
1323 0 : pcl = 0;
1324 0 : putushort(denp[1].deStartCluster, pcl);
1325 0 : putushort(denp[1].deCDate, ndirent.de_CDate);
1326 0 : putushort(denp[1].deCTime, ndirent.de_CTime);
1327 0 : denp[1].deCTimeHundredth = ndirent.de_CTimeHundredth;
1328 0 : putushort(denp[1].deADate, ndirent.de_ADate);
1329 0 : putushort(denp[1].deMDate, ndirent.de_MDate);
1330 0 : putushort(denp[1].deMTime, ndirent.de_MTime);
1331 0 : if (FAT32(pmp)) {
1332 0 : putushort(denp[0].deHighClust, newcluster >> 16);
1333 0 : putushort(denp[1].deHighClust, pdep->de_StartCluster >> 16);
1334 0 : }
1335 :
1336 0 : if ((error = bwrite(bp)) != 0)
1337 : goto bad;
1338 :
1339 : /*
1340 : * Now build up a directory entry pointing to the newly allocated
1341 : * cluster. This will be written to an empty slot in the parent
1342 : * directory.
1343 : */
1344 : #ifdef DIAGNOSTIC
1345 0 : if ((cnp->cn_flags & HASBUF) == 0)
1346 0 : panic("msdosfs_mkdir: no name");
1347 : #endif
1348 0 : if ((error = uniqdosname(pdep, cnp, ndirent.de_Name)) != 0)
1349 : goto bad;
1350 :
1351 0 : ndirent.de_Attributes = ATTR_DIRECTORY;
1352 0 : ndirent.de_StartCluster = newcluster;
1353 0 : ndirent.de_FileSize = 0;
1354 0 : ndirent.de_dev = pdep->de_dev;
1355 0 : ndirent.de_devvp = pdep->de_devvp;
1356 0 : if ((error = createde(&ndirent, pdep, &dep, cnp)) != 0)
1357 : goto bad;
1358 0 : if ((cnp->cn_flags & SAVESTART) == 0)
1359 0 : pool_put(&namei_pool, cnp->cn_pnbuf);
1360 0 : VN_KNOTE(ap->a_dvp, NOTE_WRITE | NOTE_LINK);
1361 0 : vput(ap->a_dvp);
1362 0 : *ap->a_vpp = DETOV(dep);
1363 0 : return (0);
1364 :
1365 : bad:
1366 0 : clusterfree(pmp, newcluster, NULL);
1367 : bad2:
1368 0 : pool_put(&namei_pool, cnp->cn_pnbuf);
1369 0 : vput(ap->a_dvp);
1370 0 : return (error);
1371 0 : }
1372 :
1373 : int
1374 0 : msdosfs_rmdir(void *v)
1375 : {
1376 0 : struct vop_rmdir_args *ap = v;
1377 0 : struct vnode *vp = ap->a_vp;
1378 0 : struct vnode *dvp = ap->a_dvp;
1379 0 : struct componentname *cnp = ap->a_cnp;
1380 : struct denode *ip, *dp;
1381 : int error;
1382 :
1383 0 : ip = VTODE(vp);
1384 0 : dp = VTODE(dvp);
1385 : /*
1386 : * Verify the directory is empty (and valid).
1387 : * (Rmdir ".." won't be valid since
1388 : * ".." will contain a reference to
1389 : * the current directory and thus be
1390 : * non-empty.)
1391 : */
1392 : error = 0;
1393 0 : if (!dosdirempty(ip) || ip->de_flag & DE_RENAME) {
1394 : error = ENOTEMPTY;
1395 0 : goto out;
1396 : }
1397 :
1398 0 : VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
1399 :
1400 : /*
1401 : * Delete the entry from the directory. For dos filesystems this
1402 : * gets rid of the directory entry on disk, the in memory copy
1403 : * still exists but the de_refcnt is <= 0. This prevents it from
1404 : * being found by deget(). When the vput() on dep is done we give
1405 : * up access and eventually msdosfs_reclaim() will be called which
1406 : * will remove it from the denode cache.
1407 : */
1408 0 : if ((error = removede(dp, ip)) != 0)
1409 : goto out;
1410 : /*
1411 : * This is where we decrement the link count in the parent
1412 : * directory. Since dos filesystems don't do this we just purge
1413 : * the name cache and let go of the parent directory denode.
1414 : */
1415 0 : cache_purge(dvp);
1416 0 : vput(dvp);
1417 : dvp = NULL;
1418 : /*
1419 : * Truncate the directory that is being deleted.
1420 : */
1421 0 : error = detrunc(ip, (uint32_t)0, IO_SYNC, cnp->cn_cred, cnp->cn_proc);
1422 0 : cache_purge(vp);
1423 : out:
1424 0 : if (dvp)
1425 0 : vput(dvp);
1426 0 : VN_KNOTE(vp, NOTE_DELETE);
1427 0 : vput(vp);
1428 0 : return (error);
1429 : }
1430 :
1431 : /*
1432 : * DOS filesystems don't know what symlinks are.
1433 : */
1434 : int
1435 0 : msdosfs_symlink(void *v)
1436 : {
1437 0 : struct vop_symlink_args *ap = v;
1438 :
1439 0 : VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
1440 0 : vput(ap->a_dvp);
1441 0 : return (EOPNOTSUPP);
1442 : }
1443 :
1444 : int
1445 0 : msdosfs_readdir(void *v)
1446 : {
1447 0 : struct vop_readdir_args *ap = v;
1448 : int error = 0;
1449 : int diff;
1450 : long n;
1451 0 : int blsize;
1452 : long on;
1453 : long lost;
1454 : long count;
1455 : uint32_t dirsperblk;
1456 0 : uint32_t cn, lbn;
1457 : uint32_t fileno;
1458 : long bias = 0;
1459 0 : daddr_t bn;
1460 0 : struct buf *bp;
1461 0 : struct denode *dep = VTODE(ap->a_vp);
1462 0 : struct msdosfsmount *pmp = dep->de_pmp;
1463 : struct direntry *dentp;
1464 0 : struct dirent dirbuf;
1465 0 : struct uio *uio = ap->a_uio;
1466 : off_t offset, wlast = -1;
1467 : int chksum = -1;
1468 :
1469 : #ifdef MSDOSFS_DEBUG
1470 : printf("msdosfs_readdir(): vp %p, uio %p, cred %p, eofflagp %p\n",
1471 : ap->a_vp, uio, ap->a_cred, ap->a_eofflag);
1472 : #endif
1473 :
1474 : /*
1475 : * msdosfs_readdir() won't operate properly on regular files since
1476 : * it does i/o only with the filesystem vnode, and hence can
1477 : * retrieve the wrong block from the buffer cache for a plain file.
1478 : * So, fail attempts to readdir() on a plain file.
1479 : */
1480 0 : if ((dep->de_Attributes & ATTR_DIRECTORY) == 0)
1481 0 : return (ENOTDIR);
1482 :
1483 : /*
1484 : * To be safe, initialize dirbuf
1485 : */
1486 0 : bzero(&dirbuf, sizeof(dirbuf));
1487 :
1488 : /*
1489 : * If the user buffer is smaller than the size of one dos directory
1490 : * entry or the file offset is not a multiple of the size of a
1491 : * directory entry, then we fail the read.
1492 : */
1493 0 : count = uio->uio_resid & ~(sizeof(struct direntry) - 1);
1494 0 : offset = uio->uio_offset;
1495 0 : if (count < sizeof(struct direntry) ||
1496 0 : (offset & (sizeof(struct direntry) - 1)))
1497 0 : return (EINVAL);
1498 0 : lost = uio->uio_resid - count;
1499 0 : uio->uio_resid = count;
1500 :
1501 0 : dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry);
1502 :
1503 : /*
1504 : * If they are reading from the root directory then, we simulate
1505 : * the . and .. entries since these don't exist in the root
1506 : * directory. We also set the offset bias to make up for having to
1507 : * simulate these entries. By this I mean that at file offset 64 we
1508 : * read the first entry in the root directory that lives on disk.
1509 : */
1510 0 : if (dep->de_StartCluster == MSDOSFSROOT
1511 0 : || (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk)) {
1512 : #if 0
1513 : printf("msdosfs_readdir(): going after . or .. in root dir, offset %d\n",
1514 : offset);
1515 : #endif
1516 : bias = 2 * sizeof(struct direntry);
1517 0 : if (offset < bias) {
1518 0 : for (n = (int)offset / sizeof(struct direntry);
1519 0 : n < 2; n++) {
1520 0 : if (FAT32(pmp))
1521 0 : dirbuf.d_fileno = pmp->pm_rootdirblk;
1522 : else
1523 0 : dirbuf.d_fileno = 1;
1524 0 : dirbuf.d_type = DT_DIR;
1525 0 : switch (n) {
1526 : case 0:
1527 0 : dirbuf.d_namlen = 1;
1528 0 : strlcpy(dirbuf.d_name, ".",
1529 : sizeof dirbuf.d_name);
1530 0 : break;
1531 : case 1:
1532 0 : dirbuf.d_namlen = 2;
1533 0 : strlcpy(dirbuf.d_name, "..",
1534 : sizeof dirbuf.d_name);
1535 0 : break;
1536 : }
1537 0 : dirbuf.d_reclen = DIRENT_SIZE(&dirbuf);
1538 0 : dirbuf.d_off = offset +
1539 : sizeof(struct direntry);
1540 0 : if (uio->uio_resid < dirbuf.d_reclen)
1541 : goto out;
1542 0 : error = uiomove(&dirbuf, dirbuf.d_reclen, uio);
1543 0 : if (error)
1544 : goto out;
1545 0 : offset = dirbuf.d_off;
1546 : }
1547 : }
1548 : }
1549 :
1550 0 : while (uio->uio_resid > 0) {
1551 0 : lbn = de_cluster(pmp, offset - bias);
1552 0 : on = (offset - bias) & pmp->pm_crbomask;
1553 0 : n = min(pmp->pm_bpcluster - on, uio->uio_resid);
1554 0 : diff = dep->de_FileSize - (offset - bias);
1555 0 : if (diff <= 0)
1556 : break;
1557 0 : n = min(n, diff);
1558 0 : if ((error = pcbmap(dep, lbn, &bn, &cn, &blsize)) != 0)
1559 : break;
1560 0 : error = bread(pmp->pm_devvp, bn, blsize, &bp);
1561 0 : if (error) {
1562 0 : brelse(bp);
1563 0 : return (error);
1564 : }
1565 0 : n = min(n, blsize - bp->b_resid);
1566 :
1567 : /*
1568 : * Convert from dos directory entries to fs-independent
1569 : * directory entries.
1570 : */
1571 0 : for (dentp = (struct direntry *)(bp->b_data + on);
1572 0 : (char *)dentp < bp->b_data + on + n;
1573 0 : dentp++, offset += sizeof(struct direntry)) {
1574 : #if 0
1575 : printf("rd: dentp %08x prev %08x crnt %08x deName %02x attr %02x\n",
1576 : dentp, prev, crnt, dentp->deName[0], dentp->deAttributes);
1577 : #endif
1578 : /*
1579 : * If this is an unused entry, we can stop.
1580 : */
1581 0 : if (dentp->deName[0] == SLOT_EMPTY) {
1582 0 : brelse(bp);
1583 0 : goto out;
1584 : }
1585 : /*
1586 : * Skip deleted entries.
1587 : */
1588 0 : if (dentp->deName[0] == SLOT_DELETED) {
1589 : chksum = -1;
1590 : wlast = -1;
1591 0 : continue;
1592 : }
1593 :
1594 : /*
1595 : * Handle Win95 long directory entries
1596 : */
1597 0 : if (dentp->deAttributes == ATTR_WIN95) {
1598 : struct winentry *wep;
1599 0 : if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
1600 0 : continue;
1601 0 : wep = (struct winentry *)dentp;
1602 0 : chksum = win2unixfn(wep, &dirbuf, chksum);
1603 0 : if (wep->weCnt & WIN_LAST)
1604 0 : wlast = offset;
1605 0 : continue;
1606 : }
1607 :
1608 : /*
1609 : * Skip volume labels
1610 : */
1611 0 : if (dentp->deAttributes & ATTR_VOLUME) {
1612 : chksum = -1;
1613 : wlast = -1;
1614 0 : continue;
1615 : }
1616 :
1617 : /*
1618 : * This computation of d_fileno must match
1619 : * the computation of va_fileid in
1620 : * msdosfs_getattr.
1621 : */
1622 0 : fileno = getushort(dentp->deStartCluster);
1623 0 : if (FAT32(pmp))
1624 0 : fileno |= getushort(dentp->deHighClust) << 16;
1625 :
1626 0 : if (dentp->deAttributes & ATTR_DIRECTORY) {
1627 : /* Special-case root */
1628 0 : if (fileno == MSDOSFSROOT) {
1629 0 : fileno = FAT32(pmp) ?
1630 0 : pmp->pm_rootdirblk : 1;
1631 0 : }
1632 :
1633 0 : dirbuf.d_fileno = fileno;
1634 0 : dirbuf.d_type = DT_DIR;
1635 0 : } else {
1636 0 : if (getulong(dentp->deFileSize) == 0) {
1637 : uint64_t fileno64;
1638 :
1639 0 : fileno64 = (cn == MSDOSFSROOT) ?
1640 0 : roottobn(pmp, 0) : cntobn(pmp, cn);
1641 :
1642 0 : fileno64 *= dirsperblk;
1643 0 : fileno64 += dentp -
1644 0 : (struct direntry *)bp->b_data;
1645 :
1646 0 : fileno = fileidhash(fileno64);
1647 0 : }
1648 :
1649 0 : dirbuf.d_fileno = fileno;
1650 0 : dirbuf.d_type = DT_REG;
1651 : }
1652 :
1653 0 : if (chksum != winChksum(dentp->deName))
1654 0 : dirbuf.d_namlen = dos2unixfn(dentp->deName,
1655 0 : (u_char *)dirbuf.d_name,
1656 0 : pmp->pm_flags & MSDOSFSMNT_SHORTNAME);
1657 : else
1658 0 : dirbuf.d_name[dirbuf.d_namlen] = 0;
1659 : chksum = -1;
1660 0 : dirbuf.d_reclen = DIRENT_SIZE(&dirbuf);
1661 0 : dirbuf.d_off = offset + sizeof(struct direntry);
1662 0 : if (uio->uio_resid < dirbuf.d_reclen) {
1663 0 : brelse(bp);
1664 : /* Remember long-name offset. */
1665 0 : if (wlast != -1)
1666 0 : offset = wlast;
1667 : goto out;
1668 : }
1669 : wlast = -1;
1670 0 : error = uiomove(&dirbuf, dirbuf.d_reclen, uio);
1671 0 : if (error) {
1672 0 : brelse(bp);
1673 0 : goto out;
1674 : }
1675 : }
1676 0 : brelse(bp);
1677 : }
1678 :
1679 : out:
1680 0 : uio->uio_offset = offset;
1681 0 : uio->uio_resid += lost;
1682 0 : if (dep->de_FileSize - (offset - bias) <= 0)
1683 0 : *ap->a_eofflag = 1;
1684 : else
1685 0 : *ap->a_eofflag = 0;
1686 0 : return (error);
1687 0 : }
1688 :
1689 : /*
1690 : * DOS filesystems don't know what symlinks are.
1691 : */
1692 : int
1693 0 : msdosfs_readlink(void *v)
1694 : {
1695 : #if 0
1696 : struct vop_readlink_args /* {
1697 : struct vnode *a_vp;
1698 : struct uio *a_uio;
1699 : struct ucred *a_cred;
1700 : } */ *ap;
1701 : #endif
1702 :
1703 0 : return (EINVAL);
1704 : }
1705 :
1706 : int
1707 0 : msdosfs_lock(void *v)
1708 : {
1709 0 : struct vop_lock_args *ap = v;
1710 0 : struct vnode *vp = ap->a_vp;
1711 :
1712 0 : return rrw_enter(&VTODE(vp)->de_lock, ap->a_flags & LK_RWFLAGS);
1713 : }
1714 :
1715 : int
1716 0 : msdosfs_unlock(void *v)
1717 : {
1718 0 : struct vop_unlock_args *ap = v;
1719 0 : struct vnode *vp = ap->a_vp;
1720 :
1721 0 : rrw_exit(&VTODE(vp)->de_lock);
1722 0 : return 0;
1723 : }
1724 :
1725 : int
1726 0 : msdosfs_islocked(void *v)
1727 : {
1728 0 : struct vop_islocked_args *ap = v;
1729 :
1730 0 : return rrw_status(&VTODE(ap->a_vp)->de_lock);
1731 : }
1732 :
1733 : /*
1734 : * vp - address of vnode file the file
1735 : * bn - which cluster we are interested in mapping to a filesystem block number
1736 : * vpp - returns the vnode for the block special file holding the filesystem
1737 : * containing the file of interest
1738 : * bnp - address of where to return the filesystem relative block number
1739 : */
1740 : int
1741 0 : msdosfs_bmap(void *v)
1742 : {
1743 0 : struct vop_bmap_args *ap = v;
1744 0 : struct denode *dep = VTODE(ap->a_vp);
1745 : uint32_t cn;
1746 :
1747 0 : if (ap->a_vpp != NULL)
1748 0 : *ap->a_vpp = dep->de_devvp;
1749 0 : if (ap->a_bnp == NULL)
1750 0 : return (0);
1751 :
1752 0 : cn = ap->a_bn;
1753 0 : if (cn != ap->a_bn)
1754 0 : return (EFBIG);
1755 :
1756 0 : return (msdosfs_bmaparray(ap->a_vp, cn, ap->a_bnp, ap->a_runp));
1757 0 : }
1758 :
1759 : int
1760 0 : msdosfs_bmaparray(struct vnode *vp, uint32_t cn, daddr_t *bnp, int *runp)
1761 : {
1762 0 : struct denode *dep = VTODE(vp);
1763 0 : struct msdosfsmount *pmp = dep->de_pmp;
1764 : struct mount *mp;
1765 : int error, maxrun = 0, run;
1766 0 : daddr_t runbn;
1767 :
1768 0 : mp = vp->v_mount;
1769 :
1770 0 : if (runp) {
1771 : /*
1772 : * XXX
1773 : * If MAXBSIZE is the largest transfer the disks can handle,
1774 : * we probably want maxrun to be 1 block less so that we
1775 : * don't create a block larger than the device can handle.
1776 : */
1777 0 : *runp = 0;
1778 0 : maxrun = min(MAXBSIZE / mp->mnt_stat.f_iosize - 1,
1779 0 : pmp->pm_maxcluster - cn);
1780 0 : }
1781 :
1782 0 : if ((error = pcbmap(dep, cn, bnp, 0, 0)) != 0)
1783 0 : return (error);
1784 :
1785 0 : for (run = 1; run <= maxrun; run++) {
1786 0 : error = pcbmap(dep, cn + run, &runbn, 0, 0);
1787 0 : if (error != 0 || (runbn != *bnp + de_cn2bn(pmp, run)))
1788 : break;
1789 : }
1790 :
1791 0 : if (runp)
1792 0 : *runp = run - 1;
1793 :
1794 0 : return (0);
1795 0 : }
1796 :
1797 : int
1798 0 : msdosfs_strategy(void *v)
1799 : {
1800 0 : struct vop_strategy_args *ap = v;
1801 0 : struct buf *bp = ap->a_bp;
1802 0 : struct denode *dep = VTODE(bp->b_vp);
1803 : struct vnode *vp;
1804 : int error = 0;
1805 : int s;
1806 :
1807 0 : if (bp->b_vp->v_type == VBLK || bp->b_vp->v_type == VCHR)
1808 0 : panic("msdosfs_strategy: spec");
1809 : /*
1810 : * If we don't already know the filesystem relative block number
1811 : * then get it using pcbmap(). If pcbmap() returns the block
1812 : * number as -1 then we've got a hole in the file. DOS filesystems
1813 : * don't allow files with holes, so we shouldn't ever see this.
1814 : */
1815 0 : if (bp->b_blkno == bp->b_lblkno) {
1816 0 : error = pcbmap(dep, bp->b_lblkno, &bp->b_blkno, 0, 0);
1817 0 : if (error)
1818 0 : bp->b_blkno = -1;
1819 0 : if (bp->b_blkno == -1)
1820 0 : clrbuf(bp);
1821 : }
1822 0 : if (bp->b_blkno == -1) {
1823 0 : s = splbio();
1824 0 : biodone(bp);
1825 0 : splx(s);
1826 0 : return (error);
1827 : }
1828 :
1829 : /*
1830 : * Read/write the block from/to the disk that contains the desired
1831 : * file block.
1832 : */
1833 :
1834 0 : vp = dep->de_devvp;
1835 0 : bp->b_dev = vp->v_rdev;
1836 0 : (vp->v_op->vop_strategy)(ap);
1837 0 : return (0);
1838 0 : }
1839 :
1840 : int
1841 0 : msdosfs_print(void *v)
1842 : {
1843 0 : struct vop_print_args *ap = v;
1844 0 : struct denode *dep = VTODE(ap->a_vp);
1845 :
1846 0 : printf(
1847 : "tag VT_MSDOSFS, startcluster %u, dircluster %u, diroffset %u ",
1848 0 : dep->de_StartCluster, dep->de_dirclust, dep->de_diroffset);
1849 0 : printf(" dev %d, %d, %s\n",
1850 0 : major(dep->de_dev), minor(dep->de_dev),
1851 0 : VOP_ISLOCKED(ap->a_vp) ? "(LOCKED)" : "");
1852 : #ifdef DIAGNOSTIC
1853 0 : printf("\n");
1854 : #endif
1855 :
1856 0 : return (0);
1857 : }
1858 :
1859 : int
1860 0 : msdosfs_advlock(void *v)
1861 : {
1862 0 : struct vop_advlock_args *ap = v;
1863 0 : struct denode *dep = VTODE(ap->a_vp);
1864 :
1865 0 : return (lf_advlock(&dep->de_lockf, dep->de_FileSize, ap->a_id, ap->a_op,
1866 0 : ap->a_fl, ap->a_flags));
1867 : }
1868 :
1869 : int
1870 0 : msdosfs_pathconf(void *v)
1871 : {
1872 0 : struct vop_pathconf_args *ap = v;
1873 0 : struct msdosfsmount *pmp = VTODE(ap->a_vp)->de_pmp;
1874 : int error = 0;
1875 :
1876 0 : switch (ap->a_name) {
1877 : case _PC_LINK_MAX:
1878 0 : *ap->a_retval = 1;
1879 0 : break;
1880 : case _PC_NAME_MAX:
1881 0 : *ap->a_retval = pmp->pm_flags & MSDOSFSMNT_LONGNAME ? WIN_MAXLEN : 12;
1882 0 : break;
1883 : case _PC_CHOWN_RESTRICTED:
1884 0 : *ap->a_retval = 1;
1885 0 : break;
1886 : case _PC_NO_TRUNC:
1887 0 : *ap->a_retval = 0;
1888 0 : break;
1889 : case _PC_TIMESTAMP_RESOLUTION:
1890 0 : *ap->a_retval = 2000000000; /* 2 billion nanoseconds */
1891 0 : break;
1892 : default:
1893 : error = EINVAL;
1894 0 : break;
1895 : }
1896 :
1897 0 : return (error);
1898 : }
1899 :
1900 : /*
1901 : * Thomas Wang's hash function, severely hacked to always set the high
1902 : * bit on the number it returns (so no longer a proper hash function).
1903 : */
1904 : static uint32_t
1905 0 : fileidhash(uint64_t fileid)
1906 : {
1907 : uint64_t c1 = 0x6e5ea73858134343LL;
1908 : uint64_t c2 = 0xb34e8f99a2ec9ef5LL;
1909 :
1910 : /*
1911 : * We now have the original fileid value, as 64-bit value.
1912 : * We need to reduce it to 32-bits, with the top bit set.
1913 : */
1914 0 : fileid ^= ((c1 ^ fileid) >> 32);
1915 0 : fileid *= c1;
1916 0 : fileid ^= ((c2 ^ fileid) >> 31);
1917 0 : fileid *= c2;
1918 0 : fileid ^= ((c1 ^ fileid) >> 32);
1919 :
1920 0 : return (uint32_t)(fileid | 0x80000000);
1921 : }
1922 :
1923 : /* Global vfs data structures for msdosfs */
1924 : struct vops msdosfs_vops = {
1925 : .vop_lookup = msdosfs_lookup,
1926 : .vop_create = msdosfs_create,
1927 : .vop_mknod = msdosfs_mknod,
1928 : .vop_open = msdosfs_open,
1929 : .vop_close = msdosfs_close,
1930 : .vop_access = msdosfs_access,
1931 : .vop_getattr = msdosfs_getattr,
1932 : .vop_setattr = msdosfs_setattr,
1933 : .vop_read = msdosfs_read,
1934 : .vop_write = msdosfs_write,
1935 : .vop_ioctl = msdosfs_ioctl,
1936 : .vop_poll = msdosfs_poll,
1937 : .vop_kqfilter = msdosfs_kqfilter,
1938 : .vop_fsync = msdosfs_fsync,
1939 : .vop_remove = msdosfs_remove,
1940 : .vop_link = msdosfs_link,
1941 : .vop_rename = msdosfs_rename,
1942 : .vop_mkdir = msdosfs_mkdir,
1943 : .vop_rmdir = msdosfs_rmdir,
1944 : .vop_symlink = msdosfs_symlink,
1945 : .vop_readdir = msdosfs_readdir,
1946 : .vop_readlink = msdosfs_readlink,
1947 : .vop_abortop = vop_generic_abortop,
1948 : .vop_inactive = msdosfs_inactive,
1949 : .vop_reclaim = msdosfs_reclaim,
1950 : .vop_lock = msdosfs_lock,
1951 : .vop_unlock = msdosfs_unlock,
1952 : .vop_bmap = msdosfs_bmap,
1953 : .vop_strategy = msdosfs_strategy,
1954 : .vop_print = msdosfs_print,
1955 : .vop_islocked = msdosfs_islocked,
1956 : .vop_pathconf = msdosfs_pathconf,
1957 : .vop_advlock = msdosfs_advlock,
1958 : .vop_bwrite = vop_generic_bwrite,
1959 : .vop_revoke = vop_generic_revoke,
1960 : };
1961 :
1962 : struct filterops msdosfsread_filtops =
1963 : { 1, NULL, filt_msdosfsdetach, filt_msdosfsread };
1964 : struct filterops msdosfswrite_filtops =
1965 : { 1, NULL, filt_msdosfsdetach, filt_msdosfswrite };
1966 : struct filterops msdosfsvnode_filtops =
1967 : { 1, NULL, filt_msdosfsdetach, filt_msdosfsvnode };
1968 :
1969 : int
1970 0 : msdosfs_kqfilter(void *v)
1971 : {
1972 0 : struct vop_kqfilter_args *ap = v;
1973 0 : struct vnode *vp = ap->a_vp;
1974 0 : struct knote *kn = ap->a_kn;
1975 :
1976 0 : switch (kn->kn_filter) {
1977 : case EVFILT_READ:
1978 0 : kn->kn_fop = &msdosfsread_filtops;
1979 0 : break;
1980 : case EVFILT_WRITE:
1981 0 : kn->kn_fop = &msdosfswrite_filtops;
1982 0 : break;
1983 : case EVFILT_VNODE:
1984 0 : kn->kn_fop = &msdosfsvnode_filtops;
1985 0 : break;
1986 : default:
1987 0 : return (EINVAL);
1988 : }
1989 :
1990 0 : kn->kn_hook = (caddr_t)vp;
1991 :
1992 0 : SLIST_INSERT_HEAD(&vp->v_selectinfo.si_note, kn, kn_selnext);
1993 :
1994 0 : return (0);
1995 0 : }
1996 :
1997 : void
1998 0 : filt_msdosfsdetach(struct knote *kn)
1999 : {
2000 0 : struct vnode *vp = (struct vnode *)kn->kn_hook;
2001 :
2002 0 : SLIST_REMOVE(&vp->v_selectinfo.si_note, kn, knote, kn_selnext);
2003 0 : }
2004 :
2005 : int
2006 0 : filt_msdosfsread(struct knote *kn, long hint)
2007 : {
2008 0 : struct vnode *vp = (struct vnode *)kn->kn_hook;
2009 0 : struct denode *dep = VTODE(vp);
2010 :
2011 : /*
2012 : * filesystem is gone, so set the EOF flag and schedule
2013 : * the knote for deletion.
2014 : */
2015 0 : if (hint == NOTE_REVOKE) {
2016 0 : kn->kn_flags |= (EV_EOF | EV_ONESHOT);
2017 0 : return (1);
2018 : }
2019 :
2020 0 : kn->kn_data = dep->de_FileSize - kn->kn_fp->f_offset;
2021 0 : if (kn->kn_data == 0 && kn->kn_sfflags & NOTE_EOF) {
2022 0 : kn->kn_fflags |= NOTE_EOF;
2023 0 : return (1);
2024 : }
2025 0 : return (kn->kn_data != 0);
2026 0 : }
2027 :
2028 : int
2029 0 : filt_msdosfswrite(struct knote *kn, long hint)
2030 : {
2031 : /*
2032 : * filesystem is gone, so set the EOF flag and schedule
2033 : * the knote for deletion.
2034 : */
2035 0 : if (hint == NOTE_REVOKE) {
2036 0 : kn->kn_flags |= (EV_EOF | EV_ONESHOT);
2037 0 : return (1);
2038 : }
2039 :
2040 0 : kn->kn_data = 0;
2041 0 : return (1);
2042 0 : }
2043 :
2044 : int
2045 0 : filt_msdosfsvnode(struct knote *kn, long hint)
2046 : {
2047 0 : if (kn->kn_sfflags & hint)
2048 0 : kn->kn_fflags |= hint;
2049 0 : if (hint == NOTE_REVOKE) {
2050 0 : kn->kn_flags |= EV_EOF;
2051 0 : return (1);
2052 : }
2053 0 : return (kn->kn_fflags != 0);
2054 0 : }
|