Deleted Added
full compact
msdosfs_vnops.c (30492) msdosfs_vnops.c (30513)
1/* $Id: msdosfs_vnops.c,v 1.48 1997/10/16 10:48:52 phk Exp $ */
1/* $Id: msdosfs_vnops.c,v 1.49 1997/10/16 20:32:31 phk Exp $ */
2/* $NetBSD: msdosfs_vnops.c,v 1.20 1994/08/21 18:44:13 ws Exp $ */
3
4/*-
5 * Copyright (C) 1994 Wolfgang Solfrank.
6 * Copyright (C) 1994 TooLs GmbH.
7 * All rights reserved.
8 * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by TooLs GmbH.
21 * 4. The name of TooLs GmbH may not be used to endorse or promote products
22 * derived from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
30 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
32 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
33 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35/*
36 * Written by Paul Popelka (paulp@uts.amdahl.com)
37 *
38 * You can do anything you want with this software, just don't say you wrote
39 * it, and don't remove this notice.
40 *
41 * This software is provided "as is".
42 *
43 * The author supplies this software to be publicly redistributed on the
44 * understanding that the author is not responsible for the correct
45 * functioning of this software in any circumstances and is not liable for
46 * any damages caused by this software.
47 *
48 * October 1992
49 */
50
51#include <sys/param.h>
52#include <sys/systm.h>
53#include <sys/namei.h>
54#include <sys/resourcevar.h> /* defines plimit structure in proc struct */
55#include <sys/kernel.h>
56#include <sys/stat.h>
57#include <sys/buf.h>
58#include <sys/proc.h>
59#include <sys/mount.h>
60#include <sys/unistd.h>
61#include <sys/vnode.h>
62#include <sys/poll.h>
63#include <miscfs/specfs/specdev.h> /* XXX */ /* defines v_rdev */
64#include <sys/malloc.h>
65#include <sys/dirent.h>
66#include <sys/signalvar.h>
67
68#include <vm/vm.h>
69#include <vm/vm_extern.h>
70
71#include <msdosfs/bpb.h>
72#include <msdosfs/direntry.h>
73#include <msdosfs/denode.h>
74#include <msdosfs/msdosfsmount.h>
75#include <msdosfs/fat.h>
76
77/*
78 * Prototypes for MSDOSFS vnode operations
79 */
80static int msdosfs_create __P((struct vop_create_args *));
81static int msdosfs_mknod __P((struct vop_mknod_args *));
82static int msdosfs_close __P((struct vop_close_args *));
83static int msdosfs_access __P((struct vop_access_args *));
84static int msdosfs_getattr __P((struct vop_getattr_args *));
85static int msdosfs_setattr __P((struct vop_setattr_args *));
86static int msdosfs_read __P((struct vop_read_args *));
87static int msdosfs_write __P((struct vop_write_args *));
88static int msdosfs_fsync __P((struct vop_fsync_args *));
89static int msdosfs_remove __P((struct vop_remove_args *));
90static int msdosfs_link __P((struct vop_link_args *));
91static int msdosfs_rename __P((struct vop_rename_args *));
92static int msdosfs_mkdir __P((struct vop_mkdir_args *));
93static int msdosfs_rmdir __P((struct vop_rmdir_args *));
94static int msdosfs_symlink __P((struct vop_symlink_args *));
95static int msdosfs_readdir __P((struct vop_readdir_args *));
96static int msdosfs_abortop __P((struct vop_abortop_args *));
2/* $NetBSD: msdosfs_vnops.c,v 1.20 1994/08/21 18:44:13 ws Exp $ */
3
4/*-
5 * Copyright (C) 1994 Wolfgang Solfrank.
6 * Copyright (C) 1994 TooLs GmbH.
7 * All rights reserved.
8 * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by TooLs GmbH.
21 * 4. The name of TooLs GmbH may not be used to endorse or promote products
22 * derived from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
30 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
32 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
33 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35/*
36 * Written by Paul Popelka (paulp@uts.amdahl.com)
37 *
38 * You can do anything you want with this software, just don't say you wrote
39 * it, and don't remove this notice.
40 *
41 * This software is provided "as is".
42 *
43 * The author supplies this software to be publicly redistributed on the
44 * understanding that the author is not responsible for the correct
45 * functioning of this software in any circumstances and is not liable for
46 * any damages caused by this software.
47 *
48 * October 1992
49 */
50
51#include <sys/param.h>
52#include <sys/systm.h>
53#include <sys/namei.h>
54#include <sys/resourcevar.h> /* defines plimit structure in proc struct */
55#include <sys/kernel.h>
56#include <sys/stat.h>
57#include <sys/buf.h>
58#include <sys/proc.h>
59#include <sys/mount.h>
60#include <sys/unistd.h>
61#include <sys/vnode.h>
62#include <sys/poll.h>
63#include <miscfs/specfs/specdev.h> /* XXX */ /* defines v_rdev */
64#include <sys/malloc.h>
65#include <sys/dirent.h>
66#include <sys/signalvar.h>
67
68#include <vm/vm.h>
69#include <vm/vm_extern.h>
70
71#include <msdosfs/bpb.h>
72#include <msdosfs/direntry.h>
73#include <msdosfs/denode.h>
74#include <msdosfs/msdosfsmount.h>
75#include <msdosfs/fat.h>
76
77/*
78 * Prototypes for MSDOSFS vnode operations
79 */
80static int msdosfs_create __P((struct vop_create_args *));
81static int msdosfs_mknod __P((struct vop_mknod_args *));
82static int msdosfs_close __P((struct vop_close_args *));
83static int msdosfs_access __P((struct vop_access_args *));
84static int msdosfs_getattr __P((struct vop_getattr_args *));
85static int msdosfs_setattr __P((struct vop_setattr_args *));
86static int msdosfs_read __P((struct vop_read_args *));
87static int msdosfs_write __P((struct vop_write_args *));
88static int msdosfs_fsync __P((struct vop_fsync_args *));
89static int msdosfs_remove __P((struct vop_remove_args *));
90static int msdosfs_link __P((struct vop_link_args *));
91static int msdosfs_rename __P((struct vop_rename_args *));
92static int msdosfs_mkdir __P((struct vop_mkdir_args *));
93static int msdosfs_rmdir __P((struct vop_rmdir_args *));
94static int msdosfs_symlink __P((struct vop_symlink_args *));
95static int msdosfs_readdir __P((struct vop_readdir_args *));
96static int msdosfs_abortop __P((struct vop_abortop_args *));
97static int msdosfs_lock __P((struct vop_lock_args *));
98static int msdosfs_unlock __P((struct vop_unlock_args *));
99static int msdosfs_bmap __P((struct vop_bmap_args *));
100static int msdosfs_strategy __P((struct vop_strategy_args *));
101static int msdosfs_print __P((struct vop_print_args *));
97static int msdosfs_bmap __P((struct vop_bmap_args *));
98static int msdosfs_strategy __P((struct vop_strategy_args *));
99static int msdosfs_print __P((struct vop_print_args *));
102static int msdosfs_islocked __P((struct vop_islocked_args *));
103static int msdosfs_pathconf __P((struct vop_pathconf_args *ap));
104
105/*
106 * Some general notes:
107 *
108 * In the ufs filesystem the inodes, superblocks, and indirect blocks are
109 * read/written using the vnode for the filesystem. Blocks that represent
110 * the contents of a file are read/written using the vnode for the file
111 * (including directories when they are read/written as files). This
112 * presents problems for the dos filesystem because data that should be in
113 * an inode (if dos had them) resides in the directory itself. Since we
114 * must update directory entries without the benefit of having the vnode
115 * for the directory we must use the vnode for the filesystem. This means
116 * that when a directory is actually read/written (via read, write, or
117 * readdir, or seek) we must use the vnode for the filesystem instead of
118 * the vnode for the directory as would happen in ufs. This is to insure we
119 * retreive the correct block from the buffer cache since the hash value is
120 * based upon the vnode address and the desired block number.
121 */
122
123/*
124 * Create a regular file. On entry the directory to contain the file being
125 * created is locked. We must release before we return. We must also free
126 * the pathname buffer pointed at by cnp->cn_pnbuf, always on error, or
127 * only if the SAVESTART bit in cn_flags is clear on success.
128 */
129static int
130msdosfs_create(ap)
131 struct vop_create_args /* {
132 struct vnode *a_dvp;
133 struct vnode **a_vpp;
134 struct componentname *a_cnp;
135 struct vattr *a_vap;
136 } */ *ap;
137{
138 struct componentname *cnp = ap->a_cnp;
139 struct denode ndirent;
140 struct denode *dep;
141 struct denode *pdep = VTODE(ap->a_dvp);
142 struct timespec ts;
143 int error;
144
145#ifdef MSDOSFS_DEBUG
146 printf("msdosfs_create(cnp %08x, vap %08x\n", cnp, ap->a_vap);
147#endif
148
149 /*
150 * Create a directory entry for the file, then call createde() to
151 * have it installed. NOTE: DOS files are always executable. We
152 * use the absence of the owner write bit to make the file
153 * readonly.
154 */
155#ifdef DIAGNOSTIC
156 if ((cnp->cn_flags & SAVENAME) == 0)
157 panic("msdosfs_create: no name");
158#endif
159 bzero(&ndirent, sizeof(ndirent));
160 TIMEVAL_TO_TIMESPEC(&time, &ts);
161 unix2dostime(&ts, &ndirent.de_Date, &ndirent.de_Time);
162 unix2dosfn((u_char *)cnp->cn_nameptr, ndirent.de_Name, cnp->cn_namelen);
163 ndirent.de_Attributes = (ap->a_vap->va_mode & VWRITE)
164 ? ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY;
165 ndirent.de_StartCluster = 0;
166 ndirent.de_FileSize = 0;
167 ndirent.de_dev = pdep->de_dev;
168 ndirent.de_devvp = pdep->de_devvp;
169 if ((error = createde(&ndirent, pdep, &dep)) == 0) {
170 *ap->a_vpp = DETOV(dep);
171 if ((cnp->cn_flags & SAVESTART) == 0)
172 zfree(namei_zone, cnp->cn_pnbuf);
173 } else {
174 zfree(namei_zone, cnp->cn_pnbuf);
175 }
176 vput(ap->a_dvp); /* release parent dir */
177 return error;
178}
179
180static int
181msdosfs_mknod(ap)
182 struct vop_mknod_args /* {
183 struct vnode *a_dvp;
184 struct vnode **a_vpp;
185 struct componentname *a_cnp;
186 struct vattr *a_vap;
187 } */ *ap;
188{
189 int error;
190
191 switch (ap->a_vap->va_type) {
192 case VDIR:
193 error = msdosfs_mkdir((struct vop_mkdir_args *)ap);
194 break;
195
196 case VREG:
197 error = msdosfs_create((struct vop_create_args *)ap);
198 break;
199
200 default:
201 error = EINVAL;
202 zfree(namei_zone, ap->a_cnp->cn_pnbuf);
203 vput(ap->a_dvp);
204 break;
205 }
206 return error;
207}
208
209static int
210msdosfs_close(ap)
211 struct vop_close_args /* {
212 struct vnode *a_vp;
213 int a_fflag;
214 struct ucred *a_cred;
215 struct proc *a_p;
216 } */ *ap;
217{
218 struct vnode *vp = ap->a_vp;
219 struct denode *dep = VTODE(vp);
220
221 simple_lock(&vp->v_interlock);
222 if (vp->v_usecount > 1)
223 DE_TIMES(dep, &time);
224 simple_unlock(&vp->v_interlock);
225 return 0;
226}
227
228static int
229msdosfs_access(ap)
230 struct vop_access_args /* {
231 struct vnode *a_vp;
232 int a_mode;
233 struct ucred *a_cred;
234 struct proc *a_p;
235 } */ *ap;
236{
237 struct vnode *vp = ap->a_vp;
238 struct denode *dep = VTODE(ap->a_vp);
239 struct msdosfsmount *pmp = dep->de_pmp;
240 struct ucred *cred = ap->a_cred;
241 mode_t mask, file_mode, mode = ap->a_mode;
242 register gid_t *gp;
243 int i;
244
245 file_mode = (S_IXUSR|S_IXGRP|S_IXOTH) | (S_IRUSR|S_IRGRP|S_IROTH) |
246 ((dep->de_Attributes & ATTR_READONLY) ? 0 : (S_IWUSR|S_IWGRP|S_IWOTH));
247 file_mode &= pmp->pm_mask;
248
249 /*
250 * Disallow write attempts on read-only file systems;
251 * unless the file is a socket, fifo, or a block or
252 * character device resident on the file system.
253 */
254 if (mode & VWRITE) {
255 switch (vp->v_type) {
256 case VDIR:
257 case VLNK:
258 case VREG:
259 if (vp->v_mount->mnt_flag & MNT_RDONLY)
260 return (EROFS);
261 break;
262 }
263 }
264
265 /* User id 0 always gets access. */
266 if (cred->cr_uid == 0)
267 return 0;
268
269 mask = 0;
270
271 /* Otherwise, check the owner. */
272 if (cred->cr_uid == pmp->pm_uid) {
273 if (mode & VEXEC)
274 mask |= S_IXUSR;
275 if (mode & VREAD)
276 mask |= S_IRUSR;
277 if (mode & VWRITE)
278 mask |= S_IWUSR;
279 return (file_mode & mask) == mask ? 0 : EACCES;
280 }
281
282 /* Otherwise, check the groups. */
283 for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++)
284 if (pmp->pm_gid == *gp) {
285 if (mode & VEXEC)
286 mask |= S_IXGRP;
287 if (mode & VREAD)
288 mask |= S_IRGRP;
289 if (mode & VWRITE)
290 mask |= S_IWGRP;
291 return (file_mode & mask) == mask ? 0 : EACCES;
292 }
293
294 /* Otherwise, check everyone else. */
295 if (mode & VEXEC)
296 mask |= S_IXOTH;
297 if (mode & VREAD)
298 mask |= S_IROTH;
299 if (mode & VWRITE)
300 mask |= S_IWOTH;
301 return (file_mode & mask) == mask ? 0 : EACCES;
302}
303
304static int
305msdosfs_getattr(ap)
306 struct vop_getattr_args /* {
307 struct vnode *a_vp;
308 struct vattr *a_vap;
309 struct ucred *a_cred;
310 struct proc *a_p;
311 } */ *ap;
312{
313 u_int cn;
314 struct denode *dep = VTODE(ap->a_vp);
315 struct vattr *vap = ap->a_vap;
316
317 DE_TIMES(dep, &time);
318 vap->va_fsid = dep->de_dev;
319 /*
320 * The following computation of the fileid must be the same as that
321 * used in msdosfs_readdir() to compute d_fileno. If not, pwd
322 * doesn't work.
323 */
324 if (dep->de_Attributes & ATTR_DIRECTORY) {
325 if ((cn = dep->de_StartCluster) == MSDOSFSROOT)
326 cn = 1;
327 } else {
328 if ((cn = dep->de_dirclust) == MSDOSFSROOT)
329 cn = 1;
330 cn = (cn << 16) | (dep->de_diroffset & 0xffff);
331 }
332 vap->va_fileid = cn;
333 vap->va_mode = (S_IXUSR|S_IXGRP|S_IXOTH) | (S_IRUSR|S_IRGRP|S_IROTH) |
334 ((dep->de_Attributes & ATTR_READONLY) ? 0 : (S_IWUSR|S_IWGRP|S_IWOTH));
335 vap->va_mode &= dep->de_pmp->pm_mask;
336 if (dep->de_Attributes & ATTR_DIRECTORY)
337 vap->va_mode |= S_IFDIR;
338 vap->va_nlink = 1;
339 vap->va_gid = dep->de_pmp->pm_gid;
340 vap->va_uid = dep->de_pmp->pm_uid;
341 vap->va_rdev = 0;
342 vap->va_size = dep->de_FileSize;
343 dos2unixtime(dep->de_Date, dep->de_Time, &vap->va_atime);
344 vap->va_mtime = vap->va_atime;
345#if 0
346#ifndef MSDOSFS_NODIRMOD
347 if (vap->va_mode & S_IFDIR)
348 TIMEVAL_TO_TIMESPEC(&time, &vap->va_mtime);
349#endif
350#endif
351 vap->va_ctime = vap->va_atime;
352 vap->va_flags = (dep->de_Attributes & ATTR_ARCHIVE) ? 0 : SF_ARCHIVED;
353 vap->va_gen = 0;
354 vap->va_blocksize = dep->de_pmp->pm_bpcluster;
355 vap->va_bytes = (dep->de_FileSize + dep->de_pmp->pm_crbomask) &
356 ~(dep->de_pmp->pm_crbomask);
357 vap->va_type = ap->a_vp->v_type;
358 vap->va_filerev = dep->de_modrev;
359 return 0;
360}
361
362static int
363msdosfs_setattr(ap)
364 struct vop_setattr_args /* {
365 struct vnode *a_vp;
366 struct vattr *a_vap;
367 struct ucred *a_cred;
368 struct proc *a_p;
369 } */ *ap;
370{
371 struct vnode *vp = ap->a_vp;
372 struct denode *dep = VTODE(ap->a_vp);
373 struct vattr *vap = ap->a_vap;
374 struct ucred *cred = ap->a_cred;
375 int error = 0;
376
377 /*
378 * Check for unsettable attributes.
379 */
380 if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
381 (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
382 (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
383 (vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
384 return (EINVAL);
385 }
386 if (vap->va_flags != VNOVAL) {
387 if (vp->v_mount->mnt_flag & MNT_RDONLY)
388 return (EROFS);
389 if (cred->cr_uid != dep->de_pmp->pm_uid &&
390 (error = suser(cred, &ap->a_p->p_acflag)))
391 return (error);
392 /*
393 * We are very inconsistent about handling unsupported
394 * attributes. We ignored the the access time and the
395 * read and execute bits. We were strict for the other
396 * attributes.
397 *
398 * Here we are strict, stricter than ufs in not allowing
399 * users to attempt to set SF_SETTABLE bits or anyone to
400 * set unsupported bits. However, we ignore attempts to
401 * set ATTR_ARCHIVE for directories `cp -pr' from a more
402 * sensible file system attempts it a lot.
403 */
404 if (cred->cr_uid != 0) {
405 if (vap->va_flags & SF_SETTABLE)
406 return EPERM;
407 }
408 if (vap->va_flags & ~SF_ARCHIVED)
409 return EINVAL;
410 if (vap->va_flags & SF_ARCHIVED)
411 dep->de_Attributes &= ~ATTR_ARCHIVE;
412 else if (!(dep->de_Attributes & ATTR_DIRECTORY))
413 dep->de_Attributes |= ATTR_ARCHIVE;
414 dep->de_flag |= DE_MODIFIED;
415 }
416
417 if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (uid_t)VNOVAL) {
418 if (vp->v_mount->mnt_flag & MNT_RDONLY)
419 return (EROFS);
420 if ((cred->cr_uid != dep->de_pmp->pm_uid ||
421 vap->va_uid != dep->de_pmp->pm_uid ||
422 (vap->va_gid != dep->de_pmp->pm_gid &&
423 !groupmember(vap->va_gid, cred))) &&
424 (error = suser(cred, &ap->a_p->p_acflag)))
425 return error;
426 if (vap->va_uid != dep->de_pmp->pm_uid ||
427 vap->va_gid != dep->de_pmp->pm_gid)
428 return EINVAL;
429 }
430 if (vap->va_size != VNOVAL) {
431 /*
432 * Disallow write attempts on read-only file systems;
433 * unless the file is a socket, fifo, or a block or
434 * character device resident on the file system.
435 */
436 switch (vp->v_type) {
437 case VDIR:
438 return (EISDIR);
439 case VLNK:
440 case VREG:
441 if (vp->v_mount->mnt_flag & MNT_RDONLY)
442 return (EROFS);
443 break;
444 }
445 error = detrunc(dep, vap->va_size, 0, cred, ap->a_p);
446 if (error)
447 return error;
448 }
449 if (vap->va_mtime.tv_sec != VNOVAL) {
450 if (vp->v_mount->mnt_flag & MNT_RDONLY)
451 return (EROFS);
452 if (cred->cr_uid != dep->de_pmp->pm_uid &&
453 (error = suser(cred, &ap->a_p->p_acflag)) &&
454 ((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
455 (error = VOP_ACCESS(vp, VWRITE, cred, ap->a_p))))
456 return error;
457 dep->de_flag |= DE_UPDATE;
458 error = deupdat(dep, &vap->va_mtime, 1);
459 if (error)
460 return error;
461 }
462
463 /*
464 * DOS files only have the ability to have their writability
465 * attribute set, so we use the owner write bit to set the readonly
466 * attribute.
467 */
468 error = 0;
469 if (vap->va_mode != (u_short) VNOVAL) {
470 if (vp->v_mount->mnt_flag & MNT_RDONLY)
471 return (EROFS);
472 if (cred->cr_uid != dep->de_pmp->pm_uid &&
473 (error = suser(cred, &ap->a_p->p_acflag)))
474 return error;
475
476 /* We ignore the read and execute bits */
477 if (vap->va_mode & VWRITE)
478 dep->de_Attributes &= ~ATTR_READONLY;
479 else
480 dep->de_Attributes |= ATTR_READONLY;
481 dep->de_flag |= DE_MODIFIED;
482 }
483 return error;
484}
485
486static int
487msdosfs_read(ap)
488 struct vop_read_args /* {
489 struct vnode *a_vp;
490 struct uio *a_uio;
491 int a_ioflag;
492 struct ucred *a_cred;
493 } */ *ap;
494{
495 int error = 0;
496 int diff;
497 int isadir;
498 long n;
499 long on;
500 daddr_t lbn;
501 daddr_t rablock;
502 int rasize;
503 struct buf *bp;
504 struct vnode *vp = ap->a_vp;
505 struct denode *dep = VTODE(vp);
506 struct msdosfsmount *pmp = dep->de_pmp;
507 struct uio *uio = ap->a_uio;
508
509 /*
510 * If they didn't ask for any data, then we are done.
511 */
512 if (uio->uio_resid == 0)
513 return 0;
514 if (uio->uio_offset < 0)
515 return EINVAL;
516
517 isadir = dep->de_Attributes & ATTR_DIRECTORY;
518 do {
519 lbn = uio->uio_offset >> pmp->pm_cnshift;
520 on = uio->uio_offset & pmp->pm_crbomask;
521 n = min((u_long) (pmp->pm_bpcluster - on), uio->uio_resid);
522 diff = dep->de_FileSize - uio->uio_offset;
523 if (diff <= 0)
524 return 0;
525 /* convert cluster # to block # if a directory */
526 if (isadir) {
527 error = pcbmap(dep, lbn, &lbn, 0);
528 if (error)
529 return error;
530 }
531 if (diff < n)
532 n = diff;
533 /*
534 * If we are operating on a directory file then be sure to
535 * do i/o with the vnode for the filesystem instead of the
536 * vnode for the directory.
537 */
538 if (isadir) {
539 error = bread(pmp->pm_devvp, lbn, pmp->pm_bpcluster,
540 NOCRED, &bp);
541 } else {
542 rablock = lbn + 1;
543#ifdef PC98
544 /*
545 * 1024byte/sector support
546 */
547 if (pmp->pm_BytesPerSec == 1024)
548 vp->v_flag |= 0x10000;
549#endif
550 if (vp->v_lastr + 1 == lbn &&
551 rablock * pmp->pm_bpcluster < dep->de_FileSize) {
552 rasize = pmp->pm_bpcluster;
553 error = breadn(vp, lbn, pmp->pm_bpcluster,
554 &rablock, &rasize, 1,
555 NOCRED, &bp);
556 } else {
557 error = bread(vp, lbn, pmp->pm_bpcluster, NOCRED,
558 &bp);
559 }
560 vp->v_lastr = lbn;
561 }
562 n = min(n, pmp->pm_bpcluster - bp->b_resid);
563 if (error) {
564 brelse(bp);
565 return error;
566 }
567 error = uiomove(bp->b_data + on, (int) n, uio);
568 /*
569 * If we have read everything from this block or have read
570 * to end of file then we are done with this block. Mark
571 * it to say the buffer can be reused if need be.
572 */
573#if 0
574 if (n + on == pmp->pm_bpcluster ||
575 uio->uio_offset == dep->de_FileSize)
576 bp->b_flags |= B_AGE;
577#endif
578 brelse(bp);
579 } while (error == 0 && uio->uio_resid > 0 && n != 0);
580 return error;
581}
582
583/*
584 * Write data to a file or directory.
585 */
586static int
587msdosfs_write(ap)
588 struct vop_write_args /* {
589 struct vnode *a_vp;
590 struct uio *a_uio;
591 int a_ioflag;
592 struct ucred *a_cred;
593 } */ *ap;
594{
595 int n;
596 int isadir;
597 int croffset;
598 int resid;
599 int osize;
600 int error = 0;
601 u_long count;
602 daddr_t bn, lastcn;
603 struct buf *bp;
604 int ioflag = ap->a_ioflag;
605 struct uio *uio = ap->a_uio;
606 struct proc *p = uio->uio_procp;
607 struct vnode *vp = ap->a_vp;
608 struct vnode *thisvp;
609 struct denode *dep = VTODE(vp);
610 struct msdosfsmount *pmp = dep->de_pmp;
611 struct ucred *cred = ap->a_cred;
612 struct timespec ts;
613
614#ifdef MSDOSFS_DEBUG
615 printf("msdosfs_write(vp %08x, uio %08x, ioflag %08x, cred %08x\n",
616 vp, uio, ioflag, cred);
617 printf("msdosfs_write(): diroff %d, dirclust %d, startcluster %d\n",
618 dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster);
619#endif
620
621 switch (vp->v_type) {
622 case VREG:
623 if (ioflag & IO_APPEND)
624 uio->uio_offset = dep->de_FileSize;
625 isadir = 0;
626 thisvp = vp;
627 break;
628
629 case VDIR:
630 if ((ioflag & IO_SYNC) == 0)
631 panic("msdosfs_write(): non-sync directory update");
632 isadir = 1;
633 thisvp = pmp->pm_devvp;
634 break;
635
636 default:
637 panic("msdosfs_write(): bad file type");
638 break;
639 }
640
641 if (uio->uio_offset < 0)
642 return EINVAL;
643
644 if (uio->uio_resid == 0)
645 return 0;
646
647 /*
648 * If they've exceeded their filesize limit, tell them about it.
649 */
650 if (vp->v_type == VREG && p &&
651 ((uio->uio_offset + uio->uio_resid) >
652 p->p_rlimit[RLIMIT_FSIZE].rlim_cur)) {
653 psignal(p, SIGXFSZ);
654 return EFBIG;
655 }
656
657 /*
658 * If attempting to write beyond the end of the root directory we
659 * stop that here because the root directory can not grow.
660 */
661 if ((dep->de_Attributes & ATTR_DIRECTORY) &&
662 dep->de_StartCluster == MSDOSFSROOT &&
663 (uio->uio_offset + uio->uio_resid) > dep->de_FileSize)
664 return ENOSPC;
665
666 /*
667 * If the offset we are starting the write at is beyond the end of
668 * the file, then they've done a seek. Unix filesystems allow
669 * files with holes in them, DOS doesn't so we must fill the hole
670 * with zeroed blocks.
671 */
672 if (uio->uio_offset > dep->de_FileSize) {
673 error = deextend(dep, uio->uio_offset, cred);
674 if (error)
675 return error;
676 }
677
678 /*
679 * Remember some values in case the write fails.
680 */
681 resid = uio->uio_resid;
682 osize = dep->de_FileSize;
683
684
685#ifdef PC98
686 /*
687 * 1024byte/sector support
688 */
689 if (pmp->pm_BytesPerSec == 1024)
690 thisvp->v_flag |= 0x10000;
691#endif
692 /*
693 * If we write beyond the end of the file, extend it to its ultimate
694 * size ahead of the time to hopefully get a contiguous area.
695 */
696 if (uio->uio_offset + resid > osize) {
697 count = de_clcount(pmp, uio->uio_offset + resid) - de_clcount(pmp, osize);
698 if ((error = extendfile(dep, count, NULL, NULL, 0))
699 && (error != ENOSPC || (ioflag & IO_UNIT)))
700 goto errexit;
701 lastcn = dep->de_fc[FC_LASTFC].fc_frcn;
702 } else
703 lastcn = de_clcount(pmp, osize) - 1;
704
705 do {
706 bn = de_blk(pmp, uio->uio_offset);
707 if (isadir) {
708 error = pcbmap(dep, bn, &bn, 0);
709 if (error)
710 break;
711 } else if (bn > lastcn) {
712 error = ENOSPC;
713 break;
714 }
715
716 croffset = uio->uio_offset & pmp->pm_crbomask;
717 n = min(uio->uio_resid, pmp->pm_bpcluster - croffset);
718 if (uio->uio_offset + n > dep->de_FileSize) {
719 dep->de_FileSize = uio->uio_offset + n;
720 /* The object size needs to be set before buffer is allocated */
721 vnode_pager_setsize(vp, dep->de_FileSize);
722 }
723
724 if ((uio->uio_offset & pmp->pm_crbomask) == 0
725 && (de_blk(pmp, uio->uio_offset + uio->uio_resid) > de_blk(pmp, uio->uio_offset)
726 || uio->uio_offset + uio->uio_resid >= dep->de_FileSize)) {
727 /*
728 * If either the whole cluster gets written,
729 * or we write the cluster from its start beyond EOF,
730 * then no need to read data from disk.
731 */
732 bp = getblk(thisvp, bn, pmp->pm_bpcluster, 0, 0);
733 clrbuf(bp);
734 /*
735 * Do the bmap now, since pcbmap needs buffers
736 * for the fat table. (see msdosfs_strategy)
737 */
738 if (!isadir) {
739 if (bp->b_blkno == bp->b_lblkno) {
740 error = pcbmap(dep, bp->b_lblkno,
741 &bp->b_blkno, 0);
742 if (error)
743 bp->b_blkno = -1;
744 }
745 if (bp->b_blkno == -1) {
746 brelse(bp);
747 if (!error)
748 error = EIO; /* XXX */
749 break;
750 }
751 }
752 } else {
753 /*
754 * The block we need to write into exists, so read it in.
755 */
756 error = bread(thisvp, bn, pmp->pm_bpcluster, cred, &bp);
757 if (error)
758 break;
759 }
760
761 /*
762 * Should these vnode_pager_* functions be done on dir
763 * files?
764 */
765
766 /*
767 * Copy the data from user space into the buf header.
768 */
769 error = uiomove(bp->b_data + croffset, n, uio);
770
771 /*
772 * If they want this synchronous then write it and wait for
773 * it. Otherwise, if on a cluster boundary write it
774 * asynchronously so we can move on to the next block
775 * without delay. Otherwise do a delayed write because we
776 * may want to write somemore into the block later.
777 */
778 if (ioflag & IO_SYNC)
779 (void) bwrite(bp);
780 else if (n + croffset == pmp->pm_bpcluster) {
781 bawrite(bp);
782 } else
783 bdwrite(bp);
784 dep->de_flag |= DE_UPDATE;
785 } while (error == 0 && uio->uio_resid > 0);
786
787 /*
788 * If the write failed and they want us to, truncate the file back
789 * to the size it was before the write was attempted.
790 */
791errexit:
792 if (error) {
793 if (ioflag & IO_UNIT) {
794 detrunc(dep, osize, ioflag & IO_SYNC, NOCRED, NULL);
795 uio->uio_offset -= resid - uio->uio_resid;
796 uio->uio_resid = resid;
797 } else {
798 detrunc(dep, dep->de_FileSize, ioflag & IO_SYNC, NOCRED, NULL);
799 if (uio->uio_resid != resid)
800 error = 0;
801 }
802 } else if (ioflag & IO_SYNC) {
803 TIMEVAL_TO_TIMESPEC(&time, &ts);
804 error = deupdat(dep, &ts, 1);
805 }
806 return error;
807}
808
809/*
810 * Flush the blocks of a file to disk.
811 *
812 * This function is worthless for vnodes that represent directories. Maybe we
813 * could just do a sync if they try an fsync on a directory file.
814 */
815static int
816msdosfs_fsync(ap)
817 struct vop_fsync_args /* {
818 struct vnode *a_vp;
819 struct ucred *a_cred;
820 int a_waitfor;
821 struct proc *a_p;
822 } */ *ap;
823{
824 register struct vnode *vp = ap->a_vp;
825 register struct buf *bp;
826 int wait = ap->a_waitfor == MNT_WAIT;
827 struct timespec ts;
828 struct buf *nbp;
829 int s;
830
831 /*
832 * Flush all dirty buffers associated with a vnode.
833 */
834loop:
835 s = splbio();
836 for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
837 nbp = bp->b_vnbufs.le_next;
838 if ((bp->b_flags & B_BUSY))
839 continue;
840 if ((bp->b_flags & B_DELWRI) == 0)
841 panic("msdosfs_fsync: not dirty");
842 bremfree(bp);
843 bp->b_flags |= B_BUSY;
844 splx(s);
845 (void) bwrite(bp);
846 goto loop;
847 }
848 while (vp->v_numoutput) {
849 vp->v_flag |= VBWAIT;
850 (void) tsleep((caddr_t)&vp->v_numoutput, PRIBIO + 1, "msdosfsn", 0);
851 }
852#ifdef DIAGNOSTIC
853 if (vp->v_dirtyblkhd.lh_first) {
854 vprint("msdosfs_fsync: dirty", vp);
855 goto loop;
856 }
857#endif
858 splx(s);
859 TIMEVAL_TO_TIMESPEC(&time, &ts);
860 return deupdat(VTODE(vp), &ts, wait);
861}
862
863static int
864msdosfs_remove(ap)
865 struct vop_remove_args /* {
866 struct vnode *a_dvp;
867 struct vnode *a_vp;
868 struct componentname *a_cnp;
869 } */ *ap;
870{
871 int error;
872 struct denode *dep = VTODE(ap->a_vp);
873 struct denode *ddep = VTODE(ap->a_dvp);
874
875 error = removede(ddep,dep);
876#ifdef MSDOSFS_DEBUG
877 printf("msdosfs_remove(), dep %08x, v_usecount %d\n", dep, ap->a_vp->v_usecount);
878#endif
879 if (ddep == dep)
880 vrele(ap->a_vp);
881 else
882 vput(ap->a_vp); /* causes msdosfs_inactive() to be called
883 * via vrele() */
884 vput(ap->a_dvp);
885 return error;
886}
887
888/*
889 * DOS filesystems don't know what links are. But since we already called
890 * msdosfs_lookup() with create and lockparent, the parent is locked so we
891 * have to free it before we return the error.
892 */
893static int
894msdosfs_link(ap)
895 struct vop_link_args /* {
896 struct vnode *a_tdvp;
897 struct vnode *a_vp;
898 struct componentname *a_cnp;
899 } */ *ap;
900{
901 VOP_ABORTOP(ap->a_tdvp, ap->a_cnp);
902 vput(ap->a_tdvp);
903 return EOPNOTSUPP;
904}
905
906/*
907 * Renames on files require moving the denode to a new hash queue since the
908 * denode's location is used to compute which hash queue to put the file
909 * in. Unless it is a rename in place. For example "mv a b".
910 *
911 * What follows is the basic algorithm:
912 *
913 * if (file move) {
914 * if (dest file exists) {
915 * remove dest file
916 * }
917 * if (dest and src in same directory) {
918 * rewrite name in existing directory slot
919 * } else {
920 * write new entry in dest directory
921 * update offset and dirclust in denode
922 * move denode to new hash chain
923 * clear old directory entry
924 * }
925 * } else {
926 * directory move
927 * if (dest directory exists) {
928 * if (dest is not empty) {
929 * return ENOTEMPTY
930 * }
931 * remove dest directory
932 * }
933 * if (dest and src in same directory) {
934 * rewrite name in existing entry
935 * } else {
936 * be sure dest is not a child of src directory
937 * write entry in dest directory
938 * update "." and ".." in moved directory
939 * clear old directory entry for moved directory
940 * }
941 * }
942 *
943 * On entry:
944 * source's parent directory is unlocked
945 * source file or directory is unlocked
946 * destination's parent directory is locked
947 * destination file or directory is locked if it exists
948 *
949 * On exit:
950 * all denodes should be released
951 *
952 * Notes:
953 * I'm not sure how the memory containing the pathnames pointed at by the
954 * componentname structures is freed, there may be some memory bleeding
955 * for each rename done.
956 */
957static int
958msdosfs_rename(ap)
959 struct vop_rename_args /* {
960 struct vnode *a_fdvp;
961 struct vnode *a_fvp;
962 struct componentname *a_fcnp;
963 struct vnode *a_tdvp;
964 struct vnode *a_tvp;
965 struct componentname *a_tcnp;
966 } */ *ap;
967{
968 u_char toname[11];
969 int error;
970 int newparent = 0;
971 int sourceisadirectory = 0;
972 u_long cn;
973 daddr_t bn;
974 struct vnode *tvp = ap->a_tvp;
975 struct componentname *fcnp = ap->a_fcnp;
976 struct proc *p = fcnp->cn_proc;
977 struct denode *fddep; /* from file's parent directory */
978 struct denode *fdep; /* from file or directory */
979 struct denode *tddep; /* to file's parent directory */
980 struct denode *tdep; /* to file or directory */
981 struct msdosfsmount *pmp;
982 struct direntry *dotdotp;
983 struct direntry *ep;
984 struct buf *bp;
985
986 fddep = VTODE(ap->a_fdvp);
987 fdep = VTODE(ap->a_fvp);
988 tddep = VTODE(ap->a_tdvp);
989 tdep = tvp ? VTODE(tvp) : NULL;
990 pmp = fddep->de_pmp;
991
992 /* Check for cross-device rename */
993 if ((ap->a_fvp->v_mount != ap->a_tdvp->v_mount) ||
994 (tvp && (ap->a_fvp->v_mount != tvp->v_mount))) {
995 error = EXDEV;
996 goto bad;
997 }
998
999 /*
1000 * Convert the filename in tcnp into a dos filename. We copy this
1001 * into the denode and directory entry for the destination
1002 * file/directory.
1003 */
1004 unix2dosfn((u_char *) ap->a_tcnp->cn_nameptr,
1005 toname, ap->a_tcnp->cn_namelen);
1006
1007 /*
1008 * At this point this is the lock state of the denodes:
1009 * fddep referenced
1010 * fdep referenced
1011 * tddep locked
1012 * tdep locked if it exists
1013 */
1014
1015 /*
1016 * Be sure we are not renaming ".", "..", or an alias of ".". This
1017 * leads to a crippled directory tree. It's pretty tough to do a
1018 * "ls" or "pwd" with the "." directory entry missing, and "cd .."
1019 * doesn't work if the ".." entry is missing.
1020 */
1021 if (fdep->de_Attributes & ATTR_DIRECTORY) {
1022 if ((ap->a_fcnp->cn_namelen == 1
1023 && ap->a_fcnp->cn_nameptr[0] == '.')
1024 || fddep == fdep
1025 || (ap->a_fcnp->cn_flags | ap->a_tcnp->cn_flags)
1026 & ISDOTDOT) {
1027 VOP_ABORTOP(ap->a_tdvp, ap->a_tcnp);
1028 vput(ap->a_tdvp);
1029 if (tvp)
1030 vput(tvp);
1031 VOP_ABORTOP(ap->a_fdvp, ap->a_fcnp);
1032 vrele(ap->a_fdvp);
1033 vrele(ap->a_fvp);
1034 return EINVAL;
1035 }
1036 sourceisadirectory = 1;
1037 }
1038
1039 /*
1040 * If we are renaming a directory, and the directory is being moved
1041 * to another directory, then we must be sure the destination
1042 * directory is not in the subtree of the source directory. This
1043 * could orphan everything under the source directory.
1044 * doscheckpath() unlocks the destination's parent directory so we
1045 * must look it up again to relock it.
1046 */
1047 if (fddep->de_StartCluster != tddep->de_StartCluster)
1048 newparent = 1;
1049 if (sourceisadirectory && newparent) {
1050 if (tdep) {
1051 vput(ap->a_tvp);
1052 tdep = NULL;
1053 }
1054 /* doscheckpath() vput()'s tddep */
1055 error = doscheckpath(fdep, tddep);
1056 tddep = NULL;
1057 if (error)
1058 goto bad;
1059 if ((ap->a_tcnp->cn_flags & SAVESTART) == 0)
1060 panic("msdosfs_rename(): lost to startdir");
1061 error = relookup(ap->a_tdvp, &tvp, ap->a_tcnp);
1062 if (error)
1063 goto bad;
1064 tddep = VTODE(ap->a_tdvp);
1065 tdep = tvp ? VTODE(tvp) : NULL;
1066 }
1067
1068 /*
1069 * If the destination exists, then be sure its type (file or dir)
1070 * matches that of the source. And, if it is a directory make sure
1071 * it is empty. Then delete the destination.
1072 */
1073 if (tdep) {
1074 if (tdep->de_Attributes & ATTR_DIRECTORY) {
1075 if (!sourceisadirectory) {
1076 error = ENOTDIR;
1077 goto bad;
1078 }
1079 if (!dosdirempty(tdep)) {
1080 error = ENOTEMPTY;
1081 goto bad;
1082 }
1083 cache_purge(DETOV(tddep));
1084 } else { /* destination is file */
1085 if (sourceisadirectory) {
1086 error = EISDIR;
1087 goto bad;
1088 }
1089 }
1090 error = removede(tddep,tdep);
1091 if (error)
1092 goto bad;
1093 vput(ap->a_tvp);
1094 tdep = NULL;
1095 }
1096
1097 /*
1098 * If the source and destination are in the same directory then
1099 * just read in the directory entry, change the name in the
1100 * directory entry and write it back to disk.
1101 */
1102 if (newparent == 0) {
1103 /* tddep and fddep point to the same denode here */
1104 vn_lock(ap->a_fvp, LK_EXCLUSIVE, p); /* ap->a_fdvp is already locked */
1105 error = readep(fddep->de_pmp, fdep->de_dirclust,
1106 fdep->de_diroffset, &bp, &ep);
1107 if (error) {
1108 VOP_UNLOCK(ap->a_fvp, 0, p);
1109 goto bad;
1110 }
1111 bcopy(toname, ep->deName, 11);
1112 error = bwrite(bp);
1113 if (error) {
1114 VOP_UNLOCK(ap->a_fvp, 0, p);
1115 goto bad;
1116 }
1117 bcopy(toname, fdep->de_Name, 11); /* update denode */
1118 /*
1119 * fdep locked fddep and tddep point to the same denode
1120 * which is locked tdep is NULL
1121 */
1122 } else {
1123 u_long dirsize = 0L;
1124
1125 /*
1126 * If the source and destination are in different
1127 * directories, then mark the entry in the source directory
1128 * as deleted and write a new entry in the destination
1129 * directory. Then move the denode to the correct hash
1130 * chain for its new location in the filesystem. And, if
1131 * we moved a directory, then update its .. entry to point
1132 * to the new parent directory. If we moved a directory
1133 * will also insure that the directory entry on disk has a
1134 * filesize of zero.
1135 */
1136 vn_lock(ap->a_fvp, LK_EXCLUSIVE, p);
1137 bcopy(toname, fdep->de_Name, 11); /* update denode */
1138 if (fdep->de_Attributes & ATTR_DIRECTORY) {
1139 dirsize = fdep->de_FileSize;
1140 fdep->de_FileSize = 0;
1141 }
1142 error = createde(fdep, tddep, (struct denode **) 0);
1143 if (fdep->de_Attributes & ATTR_DIRECTORY) {
1144 fdep->de_FileSize = dirsize;
1145 }
1146 if (error) {
1147 /* should put back filename */
1148 VOP_UNLOCK(ap->a_fvp, 0, p);
1149 goto bad;
1150 }
1151 vn_lock(ap->a_fdvp, LK_EXCLUSIVE, p);
1152 error = readep(fddep->de_pmp, fddep->de_fndclust,
1153 fddep->de_fndoffset, &bp, &ep);
1154 if (error) {
1155 VOP_UNLOCK(ap->a_fvp, 0, p);
1156 VOP_UNLOCK(ap->a_fdvp, 0, p);
1157 goto bad;
1158 }
1159 ep->deName[0] = SLOT_DELETED;
1160 error = bwrite(bp);
1161 if (error) {
1162 VOP_UNLOCK(ap->a_fvp, 0, p);
1163 VOP_UNLOCK(ap->a_fdvp, 0, p);
1164 goto bad;
1165 }
1166 if (!sourceisadirectory) {
1167 fdep->de_dirclust = tddep->de_fndclust;
1168 fdep->de_diroffset = tddep->de_fndoffset;
1169 reinsert(fdep);
1170 }
1171 VOP_UNLOCK(ap->a_fdvp, 0, p);
1172 }
1173 /* fdep is still locked here */
1174
1175 /*
1176 * If we moved a directory to a new parent directory, then we must
1177 * fixup the ".." entry in the moved directory.
1178 */
1179 if (sourceisadirectory && newparent) {
1180 cn = fdep->de_StartCluster;
1181 if (cn == MSDOSFSROOT) {
1182 /* this should never happen */
1183 panic("msdosfs_rename(): updating .. in root directory?");
1184 } else {
1185 bn = cntobn(pmp, cn);
1186 }
1187 error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster,
1188 NOCRED, &bp);
1189 if (error) {
1190 /* should really panic here, fs is corrupt */
1191 VOP_UNLOCK(ap->a_fvp, 0, p);
1192 goto bad;
1193 }
1194 dotdotp = (struct direntry *) bp->b_data + 1;
1195 putushort(dotdotp->deStartCluster, tddep->de_StartCluster);
1196 error = bwrite(bp);
1197 VOP_UNLOCK(ap->a_fvp, 0, p);
1198 if (error) {
1199 /* should really panic here, fs is corrupt */
1200 goto bad;
1201 }
1202 } else
1203 VOP_UNLOCK(ap->a_fvp, 0, p);
1204bad: ;
1205 vrele(DETOV(fdep));
1206 vrele(DETOV(fddep));
1207 if (tdep)
1208 vput(DETOV(tdep));
1209 if (tddep)
1210 vput(DETOV(tddep));
1211 return error;
1212}
1213
1214static struct {
1215 struct direntry dot;
1216 struct direntry dotdot;
1217} dosdirtemplate = {
1218 {
1219 ". ", " ", /* the . entry */
1220 ATTR_DIRECTORY, /* file attribute */
1221 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, /* resevered */
1222 {210, 4}, {210, 4}, /* time and date */
1223 {0, 0}, /* startcluster */
1224 {0, 0, 0, 0}, /* filesize */
1225 },{
1226 ".. ", " ", /* the .. entry */
1227 ATTR_DIRECTORY, /* file attribute */
1228 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, /* resevered */
1229 {210, 4}, {210, 4}, /* time and date */
1230 {0, 0}, /* startcluster */
1231 {0, 0, 0, 0}, /* filesize */
1232 }
1233};
1234
1235static int
1236msdosfs_mkdir(ap)
1237 struct vop_mkdir_args /* {
1238 struct vnode *a_dvp;
1239 struvt vnode **a_vpp;
1240 struvt componentname *a_cnp;
1241 struct vattr *a_vap;
1242 } */ *ap;
1243{
1244 int bn;
1245 int error;
1246 u_long newcluster;
1247 struct denode *pdep;
1248 struct denode *ndep;
1249 struct direntry *denp;
1250 struct denode ndirent;
1251 struct msdosfsmount *pmp;
1252 struct buf *bp;
1253 struct timespec ts;
1254 u_short dDate, dTime;
1255
1256 pdep = VTODE(ap->a_dvp);
1257
1258 /*
1259 * If this is the root directory and there is no space left we
1260 * can't do anything. This is because the root directory can not
1261 * change size.
1262 */
1263 if (pdep->de_StartCluster == MSDOSFSROOT && pdep->de_fndclust == (u_long)-1) {
1264 zfree(namei_zone, ap->a_cnp->cn_pnbuf);
1265 vput(ap->a_dvp);
1266 return ENOSPC;
1267 }
1268
1269 pmp = pdep->de_pmp;
1270
1271 /*
1272 * Allocate a cluster to hold the about to be created directory.
1273 */
1274 error = clusteralloc(pmp, 0, 1, CLUST_EOFE, &newcluster, NULL);
1275 if (error) {
1276 zfree(namei_zone, ap->a_cnp->cn_pnbuf);
1277 vput(ap->a_dvp);
1278 return error;
1279 }
1280
1281 /*
1282 * Now fill the cluster with the "." and ".." entries. And write
1283 * the cluster to disk. This way it is there for the parent
1284 * directory to be pointing at if there were a crash.
1285 */
1286 bn = cntobn(pmp, newcluster);
1287 /* always succeeds */
1288 bp = getblk(pmp->pm_devvp, bn, pmp->pm_bpcluster, 0, 0);
1289 bzero(bp->b_data, pmp->pm_bpcluster);
1290 bcopy(&dosdirtemplate, bp->b_data, sizeof dosdirtemplate);
1291 denp = (struct direntry *) bp->b_data;
1292 putushort(denp->deStartCluster, newcluster);
1293 TIMEVAL_TO_TIMESPEC(&time, &ts);
1294 unix2dostime(&ts, &dDate, &dTime);
1295 putushort(denp->deDate, dDate);
1296 putushort(denp->deTime, dTime);
1297 denp++;
1298 putushort(denp->deStartCluster, pdep->de_StartCluster);
1299 putushort(denp->deDate, dDate);
1300 putushort(denp->deTime, dTime);
1301 error = bwrite(bp);
1302 if (error) {
1303 clusterfree(pmp, newcluster, NULL);
1304 zfree(namei_zone, ap->a_cnp->cn_pnbuf);
1305 vput(ap->a_dvp);
1306 return error;
1307 }
1308
1309 /*
1310 * Now build up a directory entry pointing to the newly allocated
1311 * cluster. This will be written to an empty slot in the parent
1312 * directory.
1313 */
1314 ndep = &ndirent;
1315 bzero(ndep, sizeof(*ndep));
1316 unix2dosfn((u_char *)ap->a_cnp->cn_nameptr,
1317 ndep->de_Name, ap->a_cnp->cn_namelen);
1318 TIMEVAL_TO_TIMESPEC(&time, &ts);
1319 unix2dostime(&ts, &ndep->de_Date, &ndep->de_Time);
1320 ndep->de_StartCluster = newcluster;
1321 ndep->de_Attributes = ATTR_DIRECTORY;
1322
1323 error = createde(ndep, pdep, &ndep);
1324 if (error) {
1325 clusterfree(pmp, newcluster, NULL);
1326 } else {
1327 *ap->a_vpp = DETOV(ndep);
1328 }
1329 zfree(namei_zone, ap->a_cnp->cn_pnbuf);
1330#ifdef MSDOSFS_DEBUG
1331 printf("msdosfs_mkdir(): vput(%08x)\n", ap->a_dvp);
1332#endif
1333 vput(ap->a_dvp);
1334 return error;
1335}
1336
1337static int
1338msdosfs_rmdir(ap)
1339 struct vop_rmdir_args /* {
1340 struct vnode *a_dvp;
1341 struct vnode *a_vp;
1342 struct componentname *a_cnp;
1343 } */ *ap;
1344{
1345 struct denode *ddep;
1346 struct denode *dep;
1347 int error = 0;
1348
1349 ddep = VTODE(ap->a_dvp); /* parent dir of dir to delete */
1350 dep = VTODE(ap->a_vp);/* directory to delete */
1351
1352 /*
1353 * Be sure the directory being deleted is empty.
1354 */
1355 if (dosdirempty(dep) == 0) {
1356 error = ENOTEMPTY;
1357 goto out;
1358 }
1359
1360 /*
1361 * Delete the entry from the directory. For dos filesystems this
1362 * gets rid of the directory entry on disk, the in memory copy
1363 * still exists but the de_refcnt is <= 0. This prevents it from
1364 * being found by deget(). When the vput() on dep is done we give
1365 * up access and eventually msdosfs_reclaim() will be called which
1366 * will remove it from the denode cache.
1367 */
1368 error = removede(ddep,dep);
1369 if (error)
1370 goto out;
1371
1372 /*
1373 * This is where we decrement the link count in the parent
1374 * directory. Since dos filesystems don't do this we just purge
1375 * the name cache and let go of the parent directory denode.
1376 */
1377 cache_purge(DETOV(ddep));
1378 vput(ap->a_dvp);
1379 ap->a_dvp = NULL;
1380
1381 /*
1382 * Truncate the directory that is being deleted.
1383 */
1384 error = detrunc(dep, (u_long) 0, IO_SYNC, NOCRED, NULL);
1385 cache_purge(DETOV(dep));
1386
1387out: ;
1388 if (ap->a_dvp)
1389 vput(ap->a_dvp);
1390 vput(ap->a_vp);
1391 return error;
1392}
1393
1394/*
1395 * DOS filesystems don't know what symlinks are.
1396 */
1397static int
1398msdosfs_symlink(ap)
1399 struct vop_symlink_args /* {
1400 struct vnode *a_dvp;
1401 struct vnode **a_vpp;
1402 struct componentname *a_cnp;
1403 struct vattr *a_vap;
1404 char *a_target;
1405 } */ *ap;
1406{
1407 zfree(namei_zone, ap->a_cnp->cn_pnbuf);
1408 vput(ap->a_dvp);
1409 return EINVAL;
1410}
1411
1412/*
1413 * Dummy dirents to simulate the "." and ".." entries of the root directory
1414 * in a dos filesystem. Dos doesn't provide these. Note that each entry
1415 * must be the same size as a dos directory entry (32 bytes).
1416 */
1417static struct dos_dirent {
1418 u_long d_fileno;
1419 u_short d_reclen;
1420 u_char d_type;
1421 u_char d_namlen;
1422 u_char d_name[24];
1423} rootdots[2] = {
1424
1425 {
1426 1, /* d_fileno */
1427 sizeof(struct direntry), /* d_reclen */
1428 DT_DIR, /* d_type */
1429 1, /* d_namlen */
1430 "." /* d_name */
1431 },
1432 {
1433 1, /* d_fileno */
1434 sizeof(struct direntry), /* d_reclen */
1435 DT_DIR, /* d_type */
1436 2, /* d_namlen */
1437 ".." /* d_name */
1438 }
1439};
1440
1441static int
1442msdosfs_readdir(ap)
1443 struct vop_readdir_args /* {
1444 struct vnode *a_vp;
1445 struct uio *a_uio;
1446 struct ucred *a_cred;
1447 int *a_eofflag;
1448 int *a_ncookies;
1449 u_long **a_cookies;
1450 } */ *ap;
1451{
1452 int error = 0;
1453 int diff;
1454 char pushout;
1455 long n;
1456 long on;
1457 long lost;
1458 long count;
1459 u_long cn;
1460 u_long fileno;
1461 long bias = 0;
1462 daddr_t bn;
1463 daddr_t lbn;
1464 struct buf *bp;
1465 struct denode *dep = VTODE(ap->a_vp);
1466 struct msdosfsmount *pmp = dep->de_pmp;
1467 struct direntry *dentp;
1468 struct dirent *prev;
1469 struct dirent *crnt;
1470 u_char dirbuf[512]; /* holds converted dos directories */
1471 struct uio *uio = ap->a_uio;
1472 off_t off;
1473 int ncookies = 0;
1474
1475#ifdef MSDOSFS_DEBUG
1476 printf("msdosfs_readdir(): vp %08x, uio %08x, cred %08x, eofflagp %08x\n",
1477 ap->a_vp, uio, ap->a_cred, ap->a_eofflag);
1478#endif
1479
1480 /*
1481 * msdosfs_readdir() won't operate properly on regular files since
1482 * it does i/o only with the the filesystem vnode, and hence can
1483 * retrieve the wrong block from the buffer cache for a plain file.
1484 * So, fail attempts to readdir() on a plain file.
1485 */
1486 if ((dep->de_Attributes & ATTR_DIRECTORY) == 0)
1487 return ENOTDIR;
1488
1489 /*
1490 * If the user buffer is smaller than the size of one dos directory
1491 * entry or the file offset is not a multiple of the size of a
1492 * directory entry, then we fail the read.
1493 */
1494 count = uio->uio_resid & ~(sizeof(struct direntry) - 1);
1495 lost = uio->uio_resid - count;
1496 if (count < sizeof(struct direntry) ||
1497 (uio->uio_offset & (sizeof(struct direntry) - 1)))
1498 return EINVAL;
1499 uio->uio_resid = count;
1500 uio->uio_iov->iov_len = count;
1501 off = uio->uio_offset;
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 if (dep->de_StartCluster == MSDOSFSROOT) {
1511 /*
1512 * printf("msdosfs_readdir(): going after . or .. in root dir, offset %d\n",
1513 * uio->uio_offset);
1514 */
1515 bias = 2 * sizeof(struct direntry);
1516 if (uio->uio_offset < 2 * sizeof(struct direntry)) {
1517 if (uio->uio_offset
1518 && uio->uio_offset != sizeof(struct direntry)) {
1519 error = EINVAL;
1520 goto out;
1521 }
1522 n = 1;
1523 if (!uio->uio_offset) {
1524 n = 2;
1525 ncookies++;
1526 }
1527 ncookies++;
1528 error = uiomove((char *) rootdots + uio->uio_offset,
1529 n * sizeof(struct direntry), uio);
1530 }
1531 }
1532 while (!error && uio->uio_resid > 0) {
1533 lbn = (uio->uio_offset - bias) >> pmp->pm_cnshift;
1534 on = (uio->uio_offset - bias) & pmp->pm_crbomask;
1535 n = min((u_long) (pmp->pm_bpcluster - on), uio->uio_resid);
1536 diff = dep->de_FileSize - (uio->uio_offset - bias);
1537 if (diff <= 0)
1538 break;
1539 if (diff < n)
1540 n = diff;
1541 error = pcbmap(dep, lbn, &bn, &cn);
1542 if (error)
1543 break;
1544 error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, &bp);
1545 n = min(n, pmp->pm_bpcluster - bp->b_resid);
1546 if (error) {
1547 brelse(bp);
1548 return error;
1549 }
1550
1551 /*
1552 * code to convert from dos directory entries to ufs
1553 * directory entries
1554 */
1555 pushout = 0;
1556 dentp = (struct direntry *)(bp->b_data + on);
1557 prev = 0;
1558 crnt = (struct dirent *) dirbuf;
1559 while ((char *) dentp < bp->b_data + on + n) {
1560 /*
1561 * printf("rd: dentp %08x prev %08x crnt %08x deName %02x attr %02x\n",
1562 * dentp, prev, crnt, dentp->deName[0], dentp->deAttributes);
1563 */
1564 /*
1565 * If we have an empty entry or a slot from a
1566 * deleted file, or a volume label entry just
1567 * concatenate its space onto the end of the
1568 * previous entry or, manufacture an empty entry if
1569 * there is no previous entry.
1570 */
1571 if (dentp->deName[0] == SLOT_EMPTY ||
1572 dentp->deName[0] == SLOT_DELETED ||
1573 (dentp->deAttributes & ATTR_VOLUME)) {
1574 if (prev) {
1575 prev->d_reclen += sizeof(struct direntry);
1576 } else {
1577 prev = crnt;
1578 prev->d_fileno = 0;
1579 prev->d_reclen = sizeof(struct direntry);
1580 prev->d_type = DT_UNKNOWN;
1581 prev->d_namlen = 0;
1582 prev->d_name[0] = 0;
1583 ncookies++;
1584 }
1585 } else {
1586 /*
1587 * this computation of d_fileno must match
1588 * the computation of va_fileid in
1589 * msdosfs_getattr
1590 */
1591 if (dentp->deAttributes & ATTR_DIRECTORY) {
1592 /* if this is the root directory */
1593 fileno = getushort(dentp->deStartCluster);
1594 if (fileno == MSDOSFSROOT)
1595 fileno = 1;
1596 } else {
1597 /*
1598 * if the file's dirent lives in
1599 * root dir
1600 */
1601 if ((fileno = cn) == MSDOSFSROOT)
1602 fileno = 1;
1603 fileno = (fileno << 16) |
1604 ((dentp - (struct direntry *) bp->b_data) & 0xffff);
1605 }
1606 crnt->d_fileno = fileno;
1607 crnt->d_reclen = sizeof(struct direntry);
1608 crnt->d_type = (dentp->deAttributes & ATTR_DIRECTORY)
1609 ? DT_DIR : DT_REG;
1610 crnt->d_namlen = dos2unixfn(dentp->deName,
1611 (u_char *)crnt->d_name);
1612 /*
1613 * printf("readdir: file %s, fileno %08x, attr %02x, start %08x\n",
1614 * crnt->d_name, crnt->d_fileno, dentp->deAttributes,
1615 * dentp->deStartCluster);
1616 */
1617 prev = crnt;
1618 ncookies++;
1619 }
1620 dentp++;
1621
1622 crnt = (struct dirent *) ((char *) crnt + sizeof(struct direntry));
1623 pushout = 1;
1624
1625 /*
1626 * If our intermediate buffer is full then copy its
1627 * contents to user space. I would just use the
1628 * buffer the buf header points to but, I'm afraid
1629 * that when we brelse() it someone else might find
1630 * it in the cache and think its contents are
1631 * valid. Maybe there is a way to invalidate the
1632 * buffer before brelse()'ing it.
1633 */
1634 if ((u_char *) crnt >= &dirbuf[sizeof dirbuf]) {
1635 pushout = 0;
1636 error = uiomove(dirbuf, sizeof(dirbuf), uio);
1637 if (error)
1638 break;
1639 prev = 0;
1640 crnt = (struct dirent *) dirbuf;
1641 }
1642 }
1643 if (pushout) {
1644 pushout = 0;
1645 error = uiomove(dirbuf, (char *) crnt - (char *) dirbuf,
1646 uio);
1647 }
1648
1649#if 0
1650 /*
1651 * If we have read everything from this block or have read
1652 * to end of file then we are done with this block. Mark
1653 * it to say the buffer can be reused if need be.
1654 */
1655 if (n + on == pmp->pm_bpcluster ||
1656 (uio->uio_offset - bias) == dep->de_FileSize)
1657 bp->b_flags |= B_AGE;
1658#endif /* if 0 */
1659 brelse(bp);
1660 if (n == 0)
1661 break;
1662 }
1663out: ;
1664 uio->uio_resid += lost;
1665 if (!error && ap->a_ncookies != NULL) {
1666 struct dirent* dpStart;
1667 struct dirent* dpEnd;
1668 struct dirent* dp;
1669 u_long *cookies;
1670 u_long *cookiep;
1671
1672 if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1)
1673 panic("msdosfs_readdir: unexpected uio from NFS server");
1674 dpStart = (struct dirent *)
1675 (uio->uio_iov->iov_base - (uio->uio_offset - off));
1676 dpEnd = (struct dirent *) uio->uio_iov->iov_base;
1677 cookies = malloc(ncookies * sizeof(*cookies), M_TEMP, M_WAITOK);
1678 for (dp = dpStart, cookiep = cookies;
1679 dp < dpEnd;
1680 dp = (struct dirent *)((caddr_t) dp + dp->d_reclen)) {
1681 off += dp->d_reclen;
1682 *cookiep++ = (u_long) off;
1683 }
1684 *ap->a_ncookies = ncookies;
1685 *ap->a_cookies = cookies;
1686 }
1687
1688 /*
1689 * Set the eofflag (NFS uses it)
1690 */
1691 if (ap->a_eofflag)
1692 if (dep->de_FileSize - (uio->uio_offset - bias) <= 0)
1693 *ap->a_eofflag = 1;
1694 else
1695 *ap->a_eofflag = 0;
1696
1697 return error;
1698}
1699
1700static int
1701msdosfs_abortop(ap)
1702 struct vop_abortop_args /* {
1703 struct vnode *a_dvp;
1704 struct componentname *a_cnp;
1705 } */ *ap;
1706{
1707 if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF)
1708 zfree(namei_zone, ap->a_cnp->cn_pnbuf);
1709 return 0;
1710}
1711
100static int msdosfs_pathconf __P((struct vop_pathconf_args *ap));
101
102/*
103 * Some general notes:
104 *
105 * In the ufs filesystem the inodes, superblocks, and indirect blocks are
106 * read/written using the vnode for the filesystem. Blocks that represent
107 * the contents of a file are read/written using the vnode for the file
108 * (including directories when they are read/written as files). This
109 * presents problems for the dos filesystem because data that should be in
110 * an inode (if dos had them) resides in the directory itself. Since we
111 * must update directory entries without the benefit of having the vnode
112 * for the directory we must use the vnode for the filesystem. This means
113 * that when a directory is actually read/written (via read, write, or
114 * readdir, or seek) we must use the vnode for the filesystem instead of
115 * the vnode for the directory as would happen in ufs. This is to insure we
116 * retreive the correct block from the buffer cache since the hash value is
117 * based upon the vnode address and the desired block number.
118 */
119
120/*
121 * Create a regular file. On entry the directory to contain the file being
122 * created is locked. We must release before we return. We must also free
123 * the pathname buffer pointed at by cnp->cn_pnbuf, always on error, or
124 * only if the SAVESTART bit in cn_flags is clear on success.
125 */
126static int
127msdosfs_create(ap)
128 struct vop_create_args /* {
129 struct vnode *a_dvp;
130 struct vnode **a_vpp;
131 struct componentname *a_cnp;
132 struct vattr *a_vap;
133 } */ *ap;
134{
135 struct componentname *cnp = ap->a_cnp;
136 struct denode ndirent;
137 struct denode *dep;
138 struct denode *pdep = VTODE(ap->a_dvp);
139 struct timespec ts;
140 int error;
141
142#ifdef MSDOSFS_DEBUG
143 printf("msdosfs_create(cnp %08x, vap %08x\n", cnp, ap->a_vap);
144#endif
145
146 /*
147 * Create a directory entry for the file, then call createde() to
148 * have it installed. NOTE: DOS files are always executable. We
149 * use the absence of the owner write bit to make the file
150 * readonly.
151 */
152#ifdef DIAGNOSTIC
153 if ((cnp->cn_flags & SAVENAME) == 0)
154 panic("msdosfs_create: no name");
155#endif
156 bzero(&ndirent, sizeof(ndirent));
157 TIMEVAL_TO_TIMESPEC(&time, &ts);
158 unix2dostime(&ts, &ndirent.de_Date, &ndirent.de_Time);
159 unix2dosfn((u_char *)cnp->cn_nameptr, ndirent.de_Name, cnp->cn_namelen);
160 ndirent.de_Attributes = (ap->a_vap->va_mode & VWRITE)
161 ? ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY;
162 ndirent.de_StartCluster = 0;
163 ndirent.de_FileSize = 0;
164 ndirent.de_dev = pdep->de_dev;
165 ndirent.de_devvp = pdep->de_devvp;
166 if ((error = createde(&ndirent, pdep, &dep)) == 0) {
167 *ap->a_vpp = DETOV(dep);
168 if ((cnp->cn_flags & SAVESTART) == 0)
169 zfree(namei_zone, cnp->cn_pnbuf);
170 } else {
171 zfree(namei_zone, cnp->cn_pnbuf);
172 }
173 vput(ap->a_dvp); /* release parent dir */
174 return error;
175}
176
177static int
178msdosfs_mknod(ap)
179 struct vop_mknod_args /* {
180 struct vnode *a_dvp;
181 struct vnode **a_vpp;
182 struct componentname *a_cnp;
183 struct vattr *a_vap;
184 } */ *ap;
185{
186 int error;
187
188 switch (ap->a_vap->va_type) {
189 case VDIR:
190 error = msdosfs_mkdir((struct vop_mkdir_args *)ap);
191 break;
192
193 case VREG:
194 error = msdosfs_create((struct vop_create_args *)ap);
195 break;
196
197 default:
198 error = EINVAL;
199 zfree(namei_zone, ap->a_cnp->cn_pnbuf);
200 vput(ap->a_dvp);
201 break;
202 }
203 return error;
204}
205
206static int
207msdosfs_close(ap)
208 struct vop_close_args /* {
209 struct vnode *a_vp;
210 int a_fflag;
211 struct ucred *a_cred;
212 struct proc *a_p;
213 } */ *ap;
214{
215 struct vnode *vp = ap->a_vp;
216 struct denode *dep = VTODE(vp);
217
218 simple_lock(&vp->v_interlock);
219 if (vp->v_usecount > 1)
220 DE_TIMES(dep, &time);
221 simple_unlock(&vp->v_interlock);
222 return 0;
223}
224
225static int
226msdosfs_access(ap)
227 struct vop_access_args /* {
228 struct vnode *a_vp;
229 int a_mode;
230 struct ucred *a_cred;
231 struct proc *a_p;
232 } */ *ap;
233{
234 struct vnode *vp = ap->a_vp;
235 struct denode *dep = VTODE(ap->a_vp);
236 struct msdosfsmount *pmp = dep->de_pmp;
237 struct ucred *cred = ap->a_cred;
238 mode_t mask, file_mode, mode = ap->a_mode;
239 register gid_t *gp;
240 int i;
241
242 file_mode = (S_IXUSR|S_IXGRP|S_IXOTH) | (S_IRUSR|S_IRGRP|S_IROTH) |
243 ((dep->de_Attributes & ATTR_READONLY) ? 0 : (S_IWUSR|S_IWGRP|S_IWOTH));
244 file_mode &= pmp->pm_mask;
245
246 /*
247 * Disallow write attempts on read-only file systems;
248 * unless the file is a socket, fifo, or a block or
249 * character device resident on the file system.
250 */
251 if (mode & VWRITE) {
252 switch (vp->v_type) {
253 case VDIR:
254 case VLNK:
255 case VREG:
256 if (vp->v_mount->mnt_flag & MNT_RDONLY)
257 return (EROFS);
258 break;
259 }
260 }
261
262 /* User id 0 always gets access. */
263 if (cred->cr_uid == 0)
264 return 0;
265
266 mask = 0;
267
268 /* Otherwise, check the owner. */
269 if (cred->cr_uid == pmp->pm_uid) {
270 if (mode & VEXEC)
271 mask |= S_IXUSR;
272 if (mode & VREAD)
273 mask |= S_IRUSR;
274 if (mode & VWRITE)
275 mask |= S_IWUSR;
276 return (file_mode & mask) == mask ? 0 : EACCES;
277 }
278
279 /* Otherwise, check the groups. */
280 for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++)
281 if (pmp->pm_gid == *gp) {
282 if (mode & VEXEC)
283 mask |= S_IXGRP;
284 if (mode & VREAD)
285 mask |= S_IRGRP;
286 if (mode & VWRITE)
287 mask |= S_IWGRP;
288 return (file_mode & mask) == mask ? 0 : EACCES;
289 }
290
291 /* Otherwise, check everyone else. */
292 if (mode & VEXEC)
293 mask |= S_IXOTH;
294 if (mode & VREAD)
295 mask |= S_IROTH;
296 if (mode & VWRITE)
297 mask |= S_IWOTH;
298 return (file_mode & mask) == mask ? 0 : EACCES;
299}
300
301static int
302msdosfs_getattr(ap)
303 struct vop_getattr_args /* {
304 struct vnode *a_vp;
305 struct vattr *a_vap;
306 struct ucred *a_cred;
307 struct proc *a_p;
308 } */ *ap;
309{
310 u_int cn;
311 struct denode *dep = VTODE(ap->a_vp);
312 struct vattr *vap = ap->a_vap;
313
314 DE_TIMES(dep, &time);
315 vap->va_fsid = dep->de_dev;
316 /*
317 * The following computation of the fileid must be the same as that
318 * used in msdosfs_readdir() to compute d_fileno. If not, pwd
319 * doesn't work.
320 */
321 if (dep->de_Attributes & ATTR_DIRECTORY) {
322 if ((cn = dep->de_StartCluster) == MSDOSFSROOT)
323 cn = 1;
324 } else {
325 if ((cn = dep->de_dirclust) == MSDOSFSROOT)
326 cn = 1;
327 cn = (cn << 16) | (dep->de_diroffset & 0xffff);
328 }
329 vap->va_fileid = cn;
330 vap->va_mode = (S_IXUSR|S_IXGRP|S_IXOTH) | (S_IRUSR|S_IRGRP|S_IROTH) |
331 ((dep->de_Attributes & ATTR_READONLY) ? 0 : (S_IWUSR|S_IWGRP|S_IWOTH));
332 vap->va_mode &= dep->de_pmp->pm_mask;
333 if (dep->de_Attributes & ATTR_DIRECTORY)
334 vap->va_mode |= S_IFDIR;
335 vap->va_nlink = 1;
336 vap->va_gid = dep->de_pmp->pm_gid;
337 vap->va_uid = dep->de_pmp->pm_uid;
338 vap->va_rdev = 0;
339 vap->va_size = dep->de_FileSize;
340 dos2unixtime(dep->de_Date, dep->de_Time, &vap->va_atime);
341 vap->va_mtime = vap->va_atime;
342#if 0
343#ifndef MSDOSFS_NODIRMOD
344 if (vap->va_mode & S_IFDIR)
345 TIMEVAL_TO_TIMESPEC(&time, &vap->va_mtime);
346#endif
347#endif
348 vap->va_ctime = vap->va_atime;
349 vap->va_flags = (dep->de_Attributes & ATTR_ARCHIVE) ? 0 : SF_ARCHIVED;
350 vap->va_gen = 0;
351 vap->va_blocksize = dep->de_pmp->pm_bpcluster;
352 vap->va_bytes = (dep->de_FileSize + dep->de_pmp->pm_crbomask) &
353 ~(dep->de_pmp->pm_crbomask);
354 vap->va_type = ap->a_vp->v_type;
355 vap->va_filerev = dep->de_modrev;
356 return 0;
357}
358
359static int
360msdosfs_setattr(ap)
361 struct vop_setattr_args /* {
362 struct vnode *a_vp;
363 struct vattr *a_vap;
364 struct ucred *a_cred;
365 struct proc *a_p;
366 } */ *ap;
367{
368 struct vnode *vp = ap->a_vp;
369 struct denode *dep = VTODE(ap->a_vp);
370 struct vattr *vap = ap->a_vap;
371 struct ucred *cred = ap->a_cred;
372 int error = 0;
373
374 /*
375 * Check for unsettable attributes.
376 */
377 if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
378 (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
379 (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
380 (vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
381 return (EINVAL);
382 }
383 if (vap->va_flags != VNOVAL) {
384 if (vp->v_mount->mnt_flag & MNT_RDONLY)
385 return (EROFS);
386 if (cred->cr_uid != dep->de_pmp->pm_uid &&
387 (error = suser(cred, &ap->a_p->p_acflag)))
388 return (error);
389 /*
390 * We are very inconsistent about handling unsupported
391 * attributes. We ignored the the access time and the
392 * read and execute bits. We were strict for the other
393 * attributes.
394 *
395 * Here we are strict, stricter than ufs in not allowing
396 * users to attempt to set SF_SETTABLE bits or anyone to
397 * set unsupported bits. However, we ignore attempts to
398 * set ATTR_ARCHIVE for directories `cp -pr' from a more
399 * sensible file system attempts it a lot.
400 */
401 if (cred->cr_uid != 0) {
402 if (vap->va_flags & SF_SETTABLE)
403 return EPERM;
404 }
405 if (vap->va_flags & ~SF_ARCHIVED)
406 return EINVAL;
407 if (vap->va_flags & SF_ARCHIVED)
408 dep->de_Attributes &= ~ATTR_ARCHIVE;
409 else if (!(dep->de_Attributes & ATTR_DIRECTORY))
410 dep->de_Attributes |= ATTR_ARCHIVE;
411 dep->de_flag |= DE_MODIFIED;
412 }
413
414 if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (uid_t)VNOVAL) {
415 if (vp->v_mount->mnt_flag & MNT_RDONLY)
416 return (EROFS);
417 if ((cred->cr_uid != dep->de_pmp->pm_uid ||
418 vap->va_uid != dep->de_pmp->pm_uid ||
419 (vap->va_gid != dep->de_pmp->pm_gid &&
420 !groupmember(vap->va_gid, cred))) &&
421 (error = suser(cred, &ap->a_p->p_acflag)))
422 return error;
423 if (vap->va_uid != dep->de_pmp->pm_uid ||
424 vap->va_gid != dep->de_pmp->pm_gid)
425 return EINVAL;
426 }
427 if (vap->va_size != VNOVAL) {
428 /*
429 * Disallow write attempts on read-only file systems;
430 * unless the file is a socket, fifo, or a block or
431 * character device resident on the file system.
432 */
433 switch (vp->v_type) {
434 case VDIR:
435 return (EISDIR);
436 case VLNK:
437 case VREG:
438 if (vp->v_mount->mnt_flag & MNT_RDONLY)
439 return (EROFS);
440 break;
441 }
442 error = detrunc(dep, vap->va_size, 0, cred, ap->a_p);
443 if (error)
444 return error;
445 }
446 if (vap->va_mtime.tv_sec != VNOVAL) {
447 if (vp->v_mount->mnt_flag & MNT_RDONLY)
448 return (EROFS);
449 if (cred->cr_uid != dep->de_pmp->pm_uid &&
450 (error = suser(cred, &ap->a_p->p_acflag)) &&
451 ((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
452 (error = VOP_ACCESS(vp, VWRITE, cred, ap->a_p))))
453 return error;
454 dep->de_flag |= DE_UPDATE;
455 error = deupdat(dep, &vap->va_mtime, 1);
456 if (error)
457 return error;
458 }
459
460 /*
461 * DOS files only have the ability to have their writability
462 * attribute set, so we use the owner write bit to set the readonly
463 * attribute.
464 */
465 error = 0;
466 if (vap->va_mode != (u_short) VNOVAL) {
467 if (vp->v_mount->mnt_flag & MNT_RDONLY)
468 return (EROFS);
469 if (cred->cr_uid != dep->de_pmp->pm_uid &&
470 (error = suser(cred, &ap->a_p->p_acflag)))
471 return error;
472
473 /* We ignore the read and execute bits */
474 if (vap->va_mode & VWRITE)
475 dep->de_Attributes &= ~ATTR_READONLY;
476 else
477 dep->de_Attributes |= ATTR_READONLY;
478 dep->de_flag |= DE_MODIFIED;
479 }
480 return error;
481}
482
483static int
484msdosfs_read(ap)
485 struct vop_read_args /* {
486 struct vnode *a_vp;
487 struct uio *a_uio;
488 int a_ioflag;
489 struct ucred *a_cred;
490 } */ *ap;
491{
492 int error = 0;
493 int diff;
494 int isadir;
495 long n;
496 long on;
497 daddr_t lbn;
498 daddr_t rablock;
499 int rasize;
500 struct buf *bp;
501 struct vnode *vp = ap->a_vp;
502 struct denode *dep = VTODE(vp);
503 struct msdosfsmount *pmp = dep->de_pmp;
504 struct uio *uio = ap->a_uio;
505
506 /*
507 * If they didn't ask for any data, then we are done.
508 */
509 if (uio->uio_resid == 0)
510 return 0;
511 if (uio->uio_offset < 0)
512 return EINVAL;
513
514 isadir = dep->de_Attributes & ATTR_DIRECTORY;
515 do {
516 lbn = uio->uio_offset >> pmp->pm_cnshift;
517 on = uio->uio_offset & pmp->pm_crbomask;
518 n = min((u_long) (pmp->pm_bpcluster - on), uio->uio_resid);
519 diff = dep->de_FileSize - uio->uio_offset;
520 if (diff <= 0)
521 return 0;
522 /* convert cluster # to block # if a directory */
523 if (isadir) {
524 error = pcbmap(dep, lbn, &lbn, 0);
525 if (error)
526 return error;
527 }
528 if (diff < n)
529 n = diff;
530 /*
531 * If we are operating on a directory file then be sure to
532 * do i/o with the vnode for the filesystem instead of the
533 * vnode for the directory.
534 */
535 if (isadir) {
536 error = bread(pmp->pm_devvp, lbn, pmp->pm_bpcluster,
537 NOCRED, &bp);
538 } else {
539 rablock = lbn + 1;
540#ifdef PC98
541 /*
542 * 1024byte/sector support
543 */
544 if (pmp->pm_BytesPerSec == 1024)
545 vp->v_flag |= 0x10000;
546#endif
547 if (vp->v_lastr + 1 == lbn &&
548 rablock * pmp->pm_bpcluster < dep->de_FileSize) {
549 rasize = pmp->pm_bpcluster;
550 error = breadn(vp, lbn, pmp->pm_bpcluster,
551 &rablock, &rasize, 1,
552 NOCRED, &bp);
553 } else {
554 error = bread(vp, lbn, pmp->pm_bpcluster, NOCRED,
555 &bp);
556 }
557 vp->v_lastr = lbn;
558 }
559 n = min(n, pmp->pm_bpcluster - bp->b_resid);
560 if (error) {
561 brelse(bp);
562 return error;
563 }
564 error = uiomove(bp->b_data + on, (int) n, uio);
565 /*
566 * If we have read everything from this block or have read
567 * to end of file then we are done with this block. Mark
568 * it to say the buffer can be reused if need be.
569 */
570#if 0
571 if (n + on == pmp->pm_bpcluster ||
572 uio->uio_offset == dep->de_FileSize)
573 bp->b_flags |= B_AGE;
574#endif
575 brelse(bp);
576 } while (error == 0 && uio->uio_resid > 0 && n != 0);
577 return error;
578}
579
580/*
581 * Write data to a file or directory.
582 */
583static int
584msdosfs_write(ap)
585 struct vop_write_args /* {
586 struct vnode *a_vp;
587 struct uio *a_uio;
588 int a_ioflag;
589 struct ucred *a_cred;
590 } */ *ap;
591{
592 int n;
593 int isadir;
594 int croffset;
595 int resid;
596 int osize;
597 int error = 0;
598 u_long count;
599 daddr_t bn, lastcn;
600 struct buf *bp;
601 int ioflag = ap->a_ioflag;
602 struct uio *uio = ap->a_uio;
603 struct proc *p = uio->uio_procp;
604 struct vnode *vp = ap->a_vp;
605 struct vnode *thisvp;
606 struct denode *dep = VTODE(vp);
607 struct msdosfsmount *pmp = dep->de_pmp;
608 struct ucred *cred = ap->a_cred;
609 struct timespec ts;
610
611#ifdef MSDOSFS_DEBUG
612 printf("msdosfs_write(vp %08x, uio %08x, ioflag %08x, cred %08x\n",
613 vp, uio, ioflag, cred);
614 printf("msdosfs_write(): diroff %d, dirclust %d, startcluster %d\n",
615 dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster);
616#endif
617
618 switch (vp->v_type) {
619 case VREG:
620 if (ioflag & IO_APPEND)
621 uio->uio_offset = dep->de_FileSize;
622 isadir = 0;
623 thisvp = vp;
624 break;
625
626 case VDIR:
627 if ((ioflag & IO_SYNC) == 0)
628 panic("msdosfs_write(): non-sync directory update");
629 isadir = 1;
630 thisvp = pmp->pm_devvp;
631 break;
632
633 default:
634 panic("msdosfs_write(): bad file type");
635 break;
636 }
637
638 if (uio->uio_offset < 0)
639 return EINVAL;
640
641 if (uio->uio_resid == 0)
642 return 0;
643
644 /*
645 * If they've exceeded their filesize limit, tell them about it.
646 */
647 if (vp->v_type == VREG && p &&
648 ((uio->uio_offset + uio->uio_resid) >
649 p->p_rlimit[RLIMIT_FSIZE].rlim_cur)) {
650 psignal(p, SIGXFSZ);
651 return EFBIG;
652 }
653
654 /*
655 * If attempting to write beyond the end of the root directory we
656 * stop that here because the root directory can not grow.
657 */
658 if ((dep->de_Attributes & ATTR_DIRECTORY) &&
659 dep->de_StartCluster == MSDOSFSROOT &&
660 (uio->uio_offset + uio->uio_resid) > dep->de_FileSize)
661 return ENOSPC;
662
663 /*
664 * If the offset we are starting the write at is beyond the end of
665 * the file, then they've done a seek. Unix filesystems allow
666 * files with holes in them, DOS doesn't so we must fill the hole
667 * with zeroed blocks.
668 */
669 if (uio->uio_offset > dep->de_FileSize) {
670 error = deextend(dep, uio->uio_offset, cred);
671 if (error)
672 return error;
673 }
674
675 /*
676 * Remember some values in case the write fails.
677 */
678 resid = uio->uio_resid;
679 osize = dep->de_FileSize;
680
681
682#ifdef PC98
683 /*
684 * 1024byte/sector support
685 */
686 if (pmp->pm_BytesPerSec == 1024)
687 thisvp->v_flag |= 0x10000;
688#endif
689 /*
690 * If we write beyond the end of the file, extend it to its ultimate
691 * size ahead of the time to hopefully get a contiguous area.
692 */
693 if (uio->uio_offset + resid > osize) {
694 count = de_clcount(pmp, uio->uio_offset + resid) - de_clcount(pmp, osize);
695 if ((error = extendfile(dep, count, NULL, NULL, 0))
696 && (error != ENOSPC || (ioflag & IO_UNIT)))
697 goto errexit;
698 lastcn = dep->de_fc[FC_LASTFC].fc_frcn;
699 } else
700 lastcn = de_clcount(pmp, osize) - 1;
701
702 do {
703 bn = de_blk(pmp, uio->uio_offset);
704 if (isadir) {
705 error = pcbmap(dep, bn, &bn, 0);
706 if (error)
707 break;
708 } else if (bn > lastcn) {
709 error = ENOSPC;
710 break;
711 }
712
713 croffset = uio->uio_offset & pmp->pm_crbomask;
714 n = min(uio->uio_resid, pmp->pm_bpcluster - croffset);
715 if (uio->uio_offset + n > dep->de_FileSize) {
716 dep->de_FileSize = uio->uio_offset + n;
717 /* The object size needs to be set before buffer is allocated */
718 vnode_pager_setsize(vp, dep->de_FileSize);
719 }
720
721 if ((uio->uio_offset & pmp->pm_crbomask) == 0
722 && (de_blk(pmp, uio->uio_offset + uio->uio_resid) > de_blk(pmp, uio->uio_offset)
723 || uio->uio_offset + uio->uio_resid >= dep->de_FileSize)) {
724 /*
725 * If either the whole cluster gets written,
726 * or we write the cluster from its start beyond EOF,
727 * then no need to read data from disk.
728 */
729 bp = getblk(thisvp, bn, pmp->pm_bpcluster, 0, 0);
730 clrbuf(bp);
731 /*
732 * Do the bmap now, since pcbmap needs buffers
733 * for the fat table. (see msdosfs_strategy)
734 */
735 if (!isadir) {
736 if (bp->b_blkno == bp->b_lblkno) {
737 error = pcbmap(dep, bp->b_lblkno,
738 &bp->b_blkno, 0);
739 if (error)
740 bp->b_blkno = -1;
741 }
742 if (bp->b_blkno == -1) {
743 brelse(bp);
744 if (!error)
745 error = EIO; /* XXX */
746 break;
747 }
748 }
749 } else {
750 /*
751 * The block we need to write into exists, so read it in.
752 */
753 error = bread(thisvp, bn, pmp->pm_bpcluster, cred, &bp);
754 if (error)
755 break;
756 }
757
758 /*
759 * Should these vnode_pager_* functions be done on dir
760 * files?
761 */
762
763 /*
764 * Copy the data from user space into the buf header.
765 */
766 error = uiomove(bp->b_data + croffset, n, uio);
767
768 /*
769 * If they want this synchronous then write it and wait for
770 * it. Otherwise, if on a cluster boundary write it
771 * asynchronously so we can move on to the next block
772 * without delay. Otherwise do a delayed write because we
773 * may want to write somemore into the block later.
774 */
775 if (ioflag & IO_SYNC)
776 (void) bwrite(bp);
777 else if (n + croffset == pmp->pm_bpcluster) {
778 bawrite(bp);
779 } else
780 bdwrite(bp);
781 dep->de_flag |= DE_UPDATE;
782 } while (error == 0 && uio->uio_resid > 0);
783
784 /*
785 * If the write failed and they want us to, truncate the file back
786 * to the size it was before the write was attempted.
787 */
788errexit:
789 if (error) {
790 if (ioflag & IO_UNIT) {
791 detrunc(dep, osize, ioflag & IO_SYNC, NOCRED, NULL);
792 uio->uio_offset -= resid - uio->uio_resid;
793 uio->uio_resid = resid;
794 } else {
795 detrunc(dep, dep->de_FileSize, ioflag & IO_SYNC, NOCRED, NULL);
796 if (uio->uio_resid != resid)
797 error = 0;
798 }
799 } else if (ioflag & IO_SYNC) {
800 TIMEVAL_TO_TIMESPEC(&time, &ts);
801 error = deupdat(dep, &ts, 1);
802 }
803 return error;
804}
805
806/*
807 * Flush the blocks of a file to disk.
808 *
809 * This function is worthless for vnodes that represent directories. Maybe we
810 * could just do a sync if they try an fsync on a directory file.
811 */
812static int
813msdosfs_fsync(ap)
814 struct vop_fsync_args /* {
815 struct vnode *a_vp;
816 struct ucred *a_cred;
817 int a_waitfor;
818 struct proc *a_p;
819 } */ *ap;
820{
821 register struct vnode *vp = ap->a_vp;
822 register struct buf *bp;
823 int wait = ap->a_waitfor == MNT_WAIT;
824 struct timespec ts;
825 struct buf *nbp;
826 int s;
827
828 /*
829 * Flush all dirty buffers associated with a vnode.
830 */
831loop:
832 s = splbio();
833 for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
834 nbp = bp->b_vnbufs.le_next;
835 if ((bp->b_flags & B_BUSY))
836 continue;
837 if ((bp->b_flags & B_DELWRI) == 0)
838 panic("msdosfs_fsync: not dirty");
839 bremfree(bp);
840 bp->b_flags |= B_BUSY;
841 splx(s);
842 (void) bwrite(bp);
843 goto loop;
844 }
845 while (vp->v_numoutput) {
846 vp->v_flag |= VBWAIT;
847 (void) tsleep((caddr_t)&vp->v_numoutput, PRIBIO + 1, "msdosfsn", 0);
848 }
849#ifdef DIAGNOSTIC
850 if (vp->v_dirtyblkhd.lh_first) {
851 vprint("msdosfs_fsync: dirty", vp);
852 goto loop;
853 }
854#endif
855 splx(s);
856 TIMEVAL_TO_TIMESPEC(&time, &ts);
857 return deupdat(VTODE(vp), &ts, wait);
858}
859
860static int
861msdosfs_remove(ap)
862 struct vop_remove_args /* {
863 struct vnode *a_dvp;
864 struct vnode *a_vp;
865 struct componentname *a_cnp;
866 } */ *ap;
867{
868 int error;
869 struct denode *dep = VTODE(ap->a_vp);
870 struct denode *ddep = VTODE(ap->a_dvp);
871
872 error = removede(ddep,dep);
873#ifdef MSDOSFS_DEBUG
874 printf("msdosfs_remove(), dep %08x, v_usecount %d\n", dep, ap->a_vp->v_usecount);
875#endif
876 if (ddep == dep)
877 vrele(ap->a_vp);
878 else
879 vput(ap->a_vp); /* causes msdosfs_inactive() to be called
880 * via vrele() */
881 vput(ap->a_dvp);
882 return error;
883}
884
885/*
886 * DOS filesystems don't know what links are. But since we already called
887 * msdosfs_lookup() with create and lockparent, the parent is locked so we
888 * have to free it before we return the error.
889 */
890static int
891msdosfs_link(ap)
892 struct vop_link_args /* {
893 struct vnode *a_tdvp;
894 struct vnode *a_vp;
895 struct componentname *a_cnp;
896 } */ *ap;
897{
898 VOP_ABORTOP(ap->a_tdvp, ap->a_cnp);
899 vput(ap->a_tdvp);
900 return EOPNOTSUPP;
901}
902
903/*
904 * Renames on files require moving the denode to a new hash queue since the
905 * denode's location is used to compute which hash queue to put the file
906 * in. Unless it is a rename in place. For example "mv a b".
907 *
908 * What follows is the basic algorithm:
909 *
910 * if (file move) {
911 * if (dest file exists) {
912 * remove dest file
913 * }
914 * if (dest and src in same directory) {
915 * rewrite name in existing directory slot
916 * } else {
917 * write new entry in dest directory
918 * update offset and dirclust in denode
919 * move denode to new hash chain
920 * clear old directory entry
921 * }
922 * } else {
923 * directory move
924 * if (dest directory exists) {
925 * if (dest is not empty) {
926 * return ENOTEMPTY
927 * }
928 * remove dest directory
929 * }
930 * if (dest and src in same directory) {
931 * rewrite name in existing entry
932 * } else {
933 * be sure dest is not a child of src directory
934 * write entry in dest directory
935 * update "." and ".." in moved directory
936 * clear old directory entry for moved directory
937 * }
938 * }
939 *
940 * On entry:
941 * source's parent directory is unlocked
942 * source file or directory is unlocked
943 * destination's parent directory is locked
944 * destination file or directory is locked if it exists
945 *
946 * On exit:
947 * all denodes should be released
948 *
949 * Notes:
950 * I'm not sure how the memory containing the pathnames pointed at by the
951 * componentname structures is freed, there may be some memory bleeding
952 * for each rename done.
953 */
954static int
955msdosfs_rename(ap)
956 struct vop_rename_args /* {
957 struct vnode *a_fdvp;
958 struct vnode *a_fvp;
959 struct componentname *a_fcnp;
960 struct vnode *a_tdvp;
961 struct vnode *a_tvp;
962 struct componentname *a_tcnp;
963 } */ *ap;
964{
965 u_char toname[11];
966 int error;
967 int newparent = 0;
968 int sourceisadirectory = 0;
969 u_long cn;
970 daddr_t bn;
971 struct vnode *tvp = ap->a_tvp;
972 struct componentname *fcnp = ap->a_fcnp;
973 struct proc *p = fcnp->cn_proc;
974 struct denode *fddep; /* from file's parent directory */
975 struct denode *fdep; /* from file or directory */
976 struct denode *tddep; /* to file's parent directory */
977 struct denode *tdep; /* to file or directory */
978 struct msdosfsmount *pmp;
979 struct direntry *dotdotp;
980 struct direntry *ep;
981 struct buf *bp;
982
983 fddep = VTODE(ap->a_fdvp);
984 fdep = VTODE(ap->a_fvp);
985 tddep = VTODE(ap->a_tdvp);
986 tdep = tvp ? VTODE(tvp) : NULL;
987 pmp = fddep->de_pmp;
988
989 /* Check for cross-device rename */
990 if ((ap->a_fvp->v_mount != ap->a_tdvp->v_mount) ||
991 (tvp && (ap->a_fvp->v_mount != tvp->v_mount))) {
992 error = EXDEV;
993 goto bad;
994 }
995
996 /*
997 * Convert the filename in tcnp into a dos filename. We copy this
998 * into the denode and directory entry for the destination
999 * file/directory.
1000 */
1001 unix2dosfn((u_char *) ap->a_tcnp->cn_nameptr,
1002 toname, ap->a_tcnp->cn_namelen);
1003
1004 /*
1005 * At this point this is the lock state of the denodes:
1006 * fddep referenced
1007 * fdep referenced
1008 * tddep locked
1009 * tdep locked if it exists
1010 */
1011
1012 /*
1013 * Be sure we are not renaming ".", "..", or an alias of ".". This
1014 * leads to a crippled directory tree. It's pretty tough to do a
1015 * "ls" or "pwd" with the "." directory entry missing, and "cd .."
1016 * doesn't work if the ".." entry is missing.
1017 */
1018 if (fdep->de_Attributes & ATTR_DIRECTORY) {
1019 if ((ap->a_fcnp->cn_namelen == 1
1020 && ap->a_fcnp->cn_nameptr[0] == '.')
1021 || fddep == fdep
1022 || (ap->a_fcnp->cn_flags | ap->a_tcnp->cn_flags)
1023 & ISDOTDOT) {
1024 VOP_ABORTOP(ap->a_tdvp, ap->a_tcnp);
1025 vput(ap->a_tdvp);
1026 if (tvp)
1027 vput(tvp);
1028 VOP_ABORTOP(ap->a_fdvp, ap->a_fcnp);
1029 vrele(ap->a_fdvp);
1030 vrele(ap->a_fvp);
1031 return EINVAL;
1032 }
1033 sourceisadirectory = 1;
1034 }
1035
1036 /*
1037 * If we are renaming a directory, and the directory is being moved
1038 * to another directory, then we must be sure the destination
1039 * directory is not in the subtree of the source directory. This
1040 * could orphan everything under the source directory.
1041 * doscheckpath() unlocks the destination's parent directory so we
1042 * must look it up again to relock it.
1043 */
1044 if (fddep->de_StartCluster != tddep->de_StartCluster)
1045 newparent = 1;
1046 if (sourceisadirectory && newparent) {
1047 if (tdep) {
1048 vput(ap->a_tvp);
1049 tdep = NULL;
1050 }
1051 /* doscheckpath() vput()'s tddep */
1052 error = doscheckpath(fdep, tddep);
1053 tddep = NULL;
1054 if (error)
1055 goto bad;
1056 if ((ap->a_tcnp->cn_flags & SAVESTART) == 0)
1057 panic("msdosfs_rename(): lost to startdir");
1058 error = relookup(ap->a_tdvp, &tvp, ap->a_tcnp);
1059 if (error)
1060 goto bad;
1061 tddep = VTODE(ap->a_tdvp);
1062 tdep = tvp ? VTODE(tvp) : NULL;
1063 }
1064
1065 /*
1066 * If the destination exists, then be sure its type (file or dir)
1067 * matches that of the source. And, if it is a directory make sure
1068 * it is empty. Then delete the destination.
1069 */
1070 if (tdep) {
1071 if (tdep->de_Attributes & ATTR_DIRECTORY) {
1072 if (!sourceisadirectory) {
1073 error = ENOTDIR;
1074 goto bad;
1075 }
1076 if (!dosdirempty(tdep)) {
1077 error = ENOTEMPTY;
1078 goto bad;
1079 }
1080 cache_purge(DETOV(tddep));
1081 } else { /* destination is file */
1082 if (sourceisadirectory) {
1083 error = EISDIR;
1084 goto bad;
1085 }
1086 }
1087 error = removede(tddep,tdep);
1088 if (error)
1089 goto bad;
1090 vput(ap->a_tvp);
1091 tdep = NULL;
1092 }
1093
1094 /*
1095 * If the source and destination are in the same directory then
1096 * just read in the directory entry, change the name in the
1097 * directory entry and write it back to disk.
1098 */
1099 if (newparent == 0) {
1100 /* tddep and fddep point to the same denode here */
1101 vn_lock(ap->a_fvp, LK_EXCLUSIVE, p); /* ap->a_fdvp is already locked */
1102 error = readep(fddep->de_pmp, fdep->de_dirclust,
1103 fdep->de_diroffset, &bp, &ep);
1104 if (error) {
1105 VOP_UNLOCK(ap->a_fvp, 0, p);
1106 goto bad;
1107 }
1108 bcopy(toname, ep->deName, 11);
1109 error = bwrite(bp);
1110 if (error) {
1111 VOP_UNLOCK(ap->a_fvp, 0, p);
1112 goto bad;
1113 }
1114 bcopy(toname, fdep->de_Name, 11); /* update denode */
1115 /*
1116 * fdep locked fddep and tddep point to the same denode
1117 * which is locked tdep is NULL
1118 */
1119 } else {
1120 u_long dirsize = 0L;
1121
1122 /*
1123 * If the source and destination are in different
1124 * directories, then mark the entry in the source directory
1125 * as deleted and write a new entry in the destination
1126 * directory. Then move the denode to the correct hash
1127 * chain for its new location in the filesystem. And, if
1128 * we moved a directory, then update its .. entry to point
1129 * to the new parent directory. If we moved a directory
1130 * will also insure that the directory entry on disk has a
1131 * filesize of zero.
1132 */
1133 vn_lock(ap->a_fvp, LK_EXCLUSIVE, p);
1134 bcopy(toname, fdep->de_Name, 11); /* update denode */
1135 if (fdep->de_Attributes & ATTR_DIRECTORY) {
1136 dirsize = fdep->de_FileSize;
1137 fdep->de_FileSize = 0;
1138 }
1139 error = createde(fdep, tddep, (struct denode **) 0);
1140 if (fdep->de_Attributes & ATTR_DIRECTORY) {
1141 fdep->de_FileSize = dirsize;
1142 }
1143 if (error) {
1144 /* should put back filename */
1145 VOP_UNLOCK(ap->a_fvp, 0, p);
1146 goto bad;
1147 }
1148 vn_lock(ap->a_fdvp, LK_EXCLUSIVE, p);
1149 error = readep(fddep->de_pmp, fddep->de_fndclust,
1150 fddep->de_fndoffset, &bp, &ep);
1151 if (error) {
1152 VOP_UNLOCK(ap->a_fvp, 0, p);
1153 VOP_UNLOCK(ap->a_fdvp, 0, p);
1154 goto bad;
1155 }
1156 ep->deName[0] = SLOT_DELETED;
1157 error = bwrite(bp);
1158 if (error) {
1159 VOP_UNLOCK(ap->a_fvp, 0, p);
1160 VOP_UNLOCK(ap->a_fdvp, 0, p);
1161 goto bad;
1162 }
1163 if (!sourceisadirectory) {
1164 fdep->de_dirclust = tddep->de_fndclust;
1165 fdep->de_diroffset = tddep->de_fndoffset;
1166 reinsert(fdep);
1167 }
1168 VOP_UNLOCK(ap->a_fdvp, 0, p);
1169 }
1170 /* fdep is still locked here */
1171
1172 /*
1173 * If we moved a directory to a new parent directory, then we must
1174 * fixup the ".." entry in the moved directory.
1175 */
1176 if (sourceisadirectory && newparent) {
1177 cn = fdep->de_StartCluster;
1178 if (cn == MSDOSFSROOT) {
1179 /* this should never happen */
1180 panic("msdosfs_rename(): updating .. in root directory?");
1181 } else {
1182 bn = cntobn(pmp, cn);
1183 }
1184 error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster,
1185 NOCRED, &bp);
1186 if (error) {
1187 /* should really panic here, fs is corrupt */
1188 VOP_UNLOCK(ap->a_fvp, 0, p);
1189 goto bad;
1190 }
1191 dotdotp = (struct direntry *) bp->b_data + 1;
1192 putushort(dotdotp->deStartCluster, tddep->de_StartCluster);
1193 error = bwrite(bp);
1194 VOP_UNLOCK(ap->a_fvp, 0, p);
1195 if (error) {
1196 /* should really panic here, fs is corrupt */
1197 goto bad;
1198 }
1199 } else
1200 VOP_UNLOCK(ap->a_fvp, 0, p);
1201bad: ;
1202 vrele(DETOV(fdep));
1203 vrele(DETOV(fddep));
1204 if (tdep)
1205 vput(DETOV(tdep));
1206 if (tddep)
1207 vput(DETOV(tddep));
1208 return error;
1209}
1210
1211static struct {
1212 struct direntry dot;
1213 struct direntry dotdot;
1214} dosdirtemplate = {
1215 {
1216 ". ", " ", /* the . entry */
1217 ATTR_DIRECTORY, /* file attribute */
1218 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, /* resevered */
1219 {210, 4}, {210, 4}, /* time and date */
1220 {0, 0}, /* startcluster */
1221 {0, 0, 0, 0}, /* filesize */
1222 },{
1223 ".. ", " ", /* the .. entry */
1224 ATTR_DIRECTORY, /* file attribute */
1225 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, /* resevered */
1226 {210, 4}, {210, 4}, /* time and date */
1227 {0, 0}, /* startcluster */
1228 {0, 0, 0, 0}, /* filesize */
1229 }
1230};
1231
1232static int
1233msdosfs_mkdir(ap)
1234 struct vop_mkdir_args /* {
1235 struct vnode *a_dvp;
1236 struvt vnode **a_vpp;
1237 struvt componentname *a_cnp;
1238 struct vattr *a_vap;
1239 } */ *ap;
1240{
1241 int bn;
1242 int error;
1243 u_long newcluster;
1244 struct denode *pdep;
1245 struct denode *ndep;
1246 struct direntry *denp;
1247 struct denode ndirent;
1248 struct msdosfsmount *pmp;
1249 struct buf *bp;
1250 struct timespec ts;
1251 u_short dDate, dTime;
1252
1253 pdep = VTODE(ap->a_dvp);
1254
1255 /*
1256 * If this is the root directory and there is no space left we
1257 * can't do anything. This is because the root directory can not
1258 * change size.
1259 */
1260 if (pdep->de_StartCluster == MSDOSFSROOT && pdep->de_fndclust == (u_long)-1) {
1261 zfree(namei_zone, ap->a_cnp->cn_pnbuf);
1262 vput(ap->a_dvp);
1263 return ENOSPC;
1264 }
1265
1266 pmp = pdep->de_pmp;
1267
1268 /*
1269 * Allocate a cluster to hold the about to be created directory.
1270 */
1271 error = clusteralloc(pmp, 0, 1, CLUST_EOFE, &newcluster, NULL);
1272 if (error) {
1273 zfree(namei_zone, ap->a_cnp->cn_pnbuf);
1274 vput(ap->a_dvp);
1275 return error;
1276 }
1277
1278 /*
1279 * Now fill the cluster with the "." and ".." entries. And write
1280 * the cluster to disk. This way it is there for the parent
1281 * directory to be pointing at if there were a crash.
1282 */
1283 bn = cntobn(pmp, newcluster);
1284 /* always succeeds */
1285 bp = getblk(pmp->pm_devvp, bn, pmp->pm_bpcluster, 0, 0);
1286 bzero(bp->b_data, pmp->pm_bpcluster);
1287 bcopy(&dosdirtemplate, bp->b_data, sizeof dosdirtemplate);
1288 denp = (struct direntry *) bp->b_data;
1289 putushort(denp->deStartCluster, newcluster);
1290 TIMEVAL_TO_TIMESPEC(&time, &ts);
1291 unix2dostime(&ts, &dDate, &dTime);
1292 putushort(denp->deDate, dDate);
1293 putushort(denp->deTime, dTime);
1294 denp++;
1295 putushort(denp->deStartCluster, pdep->de_StartCluster);
1296 putushort(denp->deDate, dDate);
1297 putushort(denp->deTime, dTime);
1298 error = bwrite(bp);
1299 if (error) {
1300 clusterfree(pmp, newcluster, NULL);
1301 zfree(namei_zone, ap->a_cnp->cn_pnbuf);
1302 vput(ap->a_dvp);
1303 return error;
1304 }
1305
1306 /*
1307 * Now build up a directory entry pointing to the newly allocated
1308 * cluster. This will be written to an empty slot in the parent
1309 * directory.
1310 */
1311 ndep = &ndirent;
1312 bzero(ndep, sizeof(*ndep));
1313 unix2dosfn((u_char *)ap->a_cnp->cn_nameptr,
1314 ndep->de_Name, ap->a_cnp->cn_namelen);
1315 TIMEVAL_TO_TIMESPEC(&time, &ts);
1316 unix2dostime(&ts, &ndep->de_Date, &ndep->de_Time);
1317 ndep->de_StartCluster = newcluster;
1318 ndep->de_Attributes = ATTR_DIRECTORY;
1319
1320 error = createde(ndep, pdep, &ndep);
1321 if (error) {
1322 clusterfree(pmp, newcluster, NULL);
1323 } else {
1324 *ap->a_vpp = DETOV(ndep);
1325 }
1326 zfree(namei_zone, ap->a_cnp->cn_pnbuf);
1327#ifdef MSDOSFS_DEBUG
1328 printf("msdosfs_mkdir(): vput(%08x)\n", ap->a_dvp);
1329#endif
1330 vput(ap->a_dvp);
1331 return error;
1332}
1333
1334static int
1335msdosfs_rmdir(ap)
1336 struct vop_rmdir_args /* {
1337 struct vnode *a_dvp;
1338 struct vnode *a_vp;
1339 struct componentname *a_cnp;
1340 } */ *ap;
1341{
1342 struct denode *ddep;
1343 struct denode *dep;
1344 int error = 0;
1345
1346 ddep = VTODE(ap->a_dvp); /* parent dir of dir to delete */
1347 dep = VTODE(ap->a_vp);/* directory to delete */
1348
1349 /*
1350 * Be sure the directory being deleted is empty.
1351 */
1352 if (dosdirempty(dep) == 0) {
1353 error = ENOTEMPTY;
1354 goto out;
1355 }
1356
1357 /*
1358 * Delete the entry from the directory. For dos filesystems this
1359 * gets rid of the directory entry on disk, the in memory copy
1360 * still exists but the de_refcnt is <= 0. This prevents it from
1361 * being found by deget(). When the vput() on dep is done we give
1362 * up access and eventually msdosfs_reclaim() will be called which
1363 * will remove it from the denode cache.
1364 */
1365 error = removede(ddep,dep);
1366 if (error)
1367 goto out;
1368
1369 /*
1370 * This is where we decrement the link count in the parent
1371 * directory. Since dos filesystems don't do this we just purge
1372 * the name cache and let go of the parent directory denode.
1373 */
1374 cache_purge(DETOV(ddep));
1375 vput(ap->a_dvp);
1376 ap->a_dvp = NULL;
1377
1378 /*
1379 * Truncate the directory that is being deleted.
1380 */
1381 error = detrunc(dep, (u_long) 0, IO_SYNC, NOCRED, NULL);
1382 cache_purge(DETOV(dep));
1383
1384out: ;
1385 if (ap->a_dvp)
1386 vput(ap->a_dvp);
1387 vput(ap->a_vp);
1388 return error;
1389}
1390
1391/*
1392 * DOS filesystems don't know what symlinks are.
1393 */
1394static int
1395msdosfs_symlink(ap)
1396 struct vop_symlink_args /* {
1397 struct vnode *a_dvp;
1398 struct vnode **a_vpp;
1399 struct componentname *a_cnp;
1400 struct vattr *a_vap;
1401 char *a_target;
1402 } */ *ap;
1403{
1404 zfree(namei_zone, ap->a_cnp->cn_pnbuf);
1405 vput(ap->a_dvp);
1406 return EINVAL;
1407}
1408
1409/*
1410 * Dummy dirents to simulate the "." and ".." entries of the root directory
1411 * in a dos filesystem. Dos doesn't provide these. Note that each entry
1412 * must be the same size as a dos directory entry (32 bytes).
1413 */
1414static struct dos_dirent {
1415 u_long d_fileno;
1416 u_short d_reclen;
1417 u_char d_type;
1418 u_char d_namlen;
1419 u_char d_name[24];
1420} rootdots[2] = {
1421
1422 {
1423 1, /* d_fileno */
1424 sizeof(struct direntry), /* d_reclen */
1425 DT_DIR, /* d_type */
1426 1, /* d_namlen */
1427 "." /* d_name */
1428 },
1429 {
1430 1, /* d_fileno */
1431 sizeof(struct direntry), /* d_reclen */
1432 DT_DIR, /* d_type */
1433 2, /* d_namlen */
1434 ".." /* d_name */
1435 }
1436};
1437
1438static int
1439msdosfs_readdir(ap)
1440 struct vop_readdir_args /* {
1441 struct vnode *a_vp;
1442 struct uio *a_uio;
1443 struct ucred *a_cred;
1444 int *a_eofflag;
1445 int *a_ncookies;
1446 u_long **a_cookies;
1447 } */ *ap;
1448{
1449 int error = 0;
1450 int diff;
1451 char pushout;
1452 long n;
1453 long on;
1454 long lost;
1455 long count;
1456 u_long cn;
1457 u_long fileno;
1458 long bias = 0;
1459 daddr_t bn;
1460 daddr_t lbn;
1461 struct buf *bp;
1462 struct denode *dep = VTODE(ap->a_vp);
1463 struct msdosfsmount *pmp = dep->de_pmp;
1464 struct direntry *dentp;
1465 struct dirent *prev;
1466 struct dirent *crnt;
1467 u_char dirbuf[512]; /* holds converted dos directories */
1468 struct uio *uio = ap->a_uio;
1469 off_t off;
1470 int ncookies = 0;
1471
1472#ifdef MSDOSFS_DEBUG
1473 printf("msdosfs_readdir(): vp %08x, uio %08x, cred %08x, eofflagp %08x\n",
1474 ap->a_vp, uio, ap->a_cred, ap->a_eofflag);
1475#endif
1476
1477 /*
1478 * msdosfs_readdir() won't operate properly on regular files since
1479 * it does i/o only with the the filesystem vnode, and hence can
1480 * retrieve the wrong block from the buffer cache for a plain file.
1481 * So, fail attempts to readdir() on a plain file.
1482 */
1483 if ((dep->de_Attributes & ATTR_DIRECTORY) == 0)
1484 return ENOTDIR;
1485
1486 /*
1487 * If the user buffer is smaller than the size of one dos directory
1488 * entry or the file offset is not a multiple of the size of a
1489 * directory entry, then we fail the read.
1490 */
1491 count = uio->uio_resid & ~(sizeof(struct direntry) - 1);
1492 lost = uio->uio_resid - count;
1493 if (count < sizeof(struct direntry) ||
1494 (uio->uio_offset & (sizeof(struct direntry) - 1)))
1495 return EINVAL;
1496 uio->uio_resid = count;
1497 uio->uio_iov->iov_len = count;
1498 off = uio->uio_offset;
1499
1500 /*
1501 * If they are reading from the root directory then, we simulate
1502 * the . and .. entries since these don't exist in the root
1503 * directory. We also set the offset bias to make up for having to
1504 * simulate these entries. By this I mean that at file offset 64 we
1505 * read the first entry in the root directory that lives on disk.
1506 */
1507 if (dep->de_StartCluster == MSDOSFSROOT) {
1508 /*
1509 * printf("msdosfs_readdir(): going after . or .. in root dir, offset %d\n",
1510 * uio->uio_offset);
1511 */
1512 bias = 2 * sizeof(struct direntry);
1513 if (uio->uio_offset < 2 * sizeof(struct direntry)) {
1514 if (uio->uio_offset
1515 && uio->uio_offset != sizeof(struct direntry)) {
1516 error = EINVAL;
1517 goto out;
1518 }
1519 n = 1;
1520 if (!uio->uio_offset) {
1521 n = 2;
1522 ncookies++;
1523 }
1524 ncookies++;
1525 error = uiomove((char *) rootdots + uio->uio_offset,
1526 n * sizeof(struct direntry), uio);
1527 }
1528 }
1529 while (!error && uio->uio_resid > 0) {
1530 lbn = (uio->uio_offset - bias) >> pmp->pm_cnshift;
1531 on = (uio->uio_offset - bias) & pmp->pm_crbomask;
1532 n = min((u_long) (pmp->pm_bpcluster - on), uio->uio_resid);
1533 diff = dep->de_FileSize - (uio->uio_offset - bias);
1534 if (diff <= 0)
1535 break;
1536 if (diff < n)
1537 n = diff;
1538 error = pcbmap(dep, lbn, &bn, &cn);
1539 if (error)
1540 break;
1541 error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, &bp);
1542 n = min(n, pmp->pm_bpcluster - bp->b_resid);
1543 if (error) {
1544 brelse(bp);
1545 return error;
1546 }
1547
1548 /*
1549 * code to convert from dos directory entries to ufs
1550 * directory entries
1551 */
1552 pushout = 0;
1553 dentp = (struct direntry *)(bp->b_data + on);
1554 prev = 0;
1555 crnt = (struct dirent *) dirbuf;
1556 while ((char *) dentp < bp->b_data + on + n) {
1557 /*
1558 * printf("rd: dentp %08x prev %08x crnt %08x deName %02x attr %02x\n",
1559 * dentp, prev, crnt, dentp->deName[0], dentp->deAttributes);
1560 */
1561 /*
1562 * If we have an empty entry or a slot from a
1563 * deleted file, or a volume label entry just
1564 * concatenate its space onto the end of the
1565 * previous entry or, manufacture an empty entry if
1566 * there is no previous entry.
1567 */
1568 if (dentp->deName[0] == SLOT_EMPTY ||
1569 dentp->deName[0] == SLOT_DELETED ||
1570 (dentp->deAttributes & ATTR_VOLUME)) {
1571 if (prev) {
1572 prev->d_reclen += sizeof(struct direntry);
1573 } else {
1574 prev = crnt;
1575 prev->d_fileno = 0;
1576 prev->d_reclen = sizeof(struct direntry);
1577 prev->d_type = DT_UNKNOWN;
1578 prev->d_namlen = 0;
1579 prev->d_name[0] = 0;
1580 ncookies++;
1581 }
1582 } else {
1583 /*
1584 * this computation of d_fileno must match
1585 * the computation of va_fileid in
1586 * msdosfs_getattr
1587 */
1588 if (dentp->deAttributes & ATTR_DIRECTORY) {
1589 /* if this is the root directory */
1590 fileno = getushort(dentp->deStartCluster);
1591 if (fileno == MSDOSFSROOT)
1592 fileno = 1;
1593 } else {
1594 /*
1595 * if the file's dirent lives in
1596 * root dir
1597 */
1598 if ((fileno = cn) == MSDOSFSROOT)
1599 fileno = 1;
1600 fileno = (fileno << 16) |
1601 ((dentp - (struct direntry *) bp->b_data) & 0xffff);
1602 }
1603 crnt->d_fileno = fileno;
1604 crnt->d_reclen = sizeof(struct direntry);
1605 crnt->d_type = (dentp->deAttributes & ATTR_DIRECTORY)
1606 ? DT_DIR : DT_REG;
1607 crnt->d_namlen = dos2unixfn(dentp->deName,
1608 (u_char *)crnt->d_name);
1609 /*
1610 * printf("readdir: file %s, fileno %08x, attr %02x, start %08x\n",
1611 * crnt->d_name, crnt->d_fileno, dentp->deAttributes,
1612 * dentp->deStartCluster);
1613 */
1614 prev = crnt;
1615 ncookies++;
1616 }
1617 dentp++;
1618
1619 crnt = (struct dirent *) ((char *) crnt + sizeof(struct direntry));
1620 pushout = 1;
1621
1622 /*
1623 * If our intermediate buffer is full then copy its
1624 * contents to user space. I would just use the
1625 * buffer the buf header points to but, I'm afraid
1626 * that when we brelse() it someone else might find
1627 * it in the cache and think its contents are
1628 * valid. Maybe there is a way to invalidate the
1629 * buffer before brelse()'ing it.
1630 */
1631 if ((u_char *) crnt >= &dirbuf[sizeof dirbuf]) {
1632 pushout = 0;
1633 error = uiomove(dirbuf, sizeof(dirbuf), uio);
1634 if (error)
1635 break;
1636 prev = 0;
1637 crnt = (struct dirent *) dirbuf;
1638 }
1639 }
1640 if (pushout) {
1641 pushout = 0;
1642 error = uiomove(dirbuf, (char *) crnt - (char *) dirbuf,
1643 uio);
1644 }
1645
1646#if 0
1647 /*
1648 * If we have read everything from this block or have read
1649 * to end of file then we are done with this block. Mark
1650 * it to say the buffer can be reused if need be.
1651 */
1652 if (n + on == pmp->pm_bpcluster ||
1653 (uio->uio_offset - bias) == dep->de_FileSize)
1654 bp->b_flags |= B_AGE;
1655#endif /* if 0 */
1656 brelse(bp);
1657 if (n == 0)
1658 break;
1659 }
1660out: ;
1661 uio->uio_resid += lost;
1662 if (!error && ap->a_ncookies != NULL) {
1663 struct dirent* dpStart;
1664 struct dirent* dpEnd;
1665 struct dirent* dp;
1666 u_long *cookies;
1667 u_long *cookiep;
1668
1669 if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1)
1670 panic("msdosfs_readdir: unexpected uio from NFS server");
1671 dpStart = (struct dirent *)
1672 (uio->uio_iov->iov_base - (uio->uio_offset - off));
1673 dpEnd = (struct dirent *) uio->uio_iov->iov_base;
1674 cookies = malloc(ncookies * sizeof(*cookies), M_TEMP, M_WAITOK);
1675 for (dp = dpStart, cookiep = cookies;
1676 dp < dpEnd;
1677 dp = (struct dirent *)((caddr_t) dp + dp->d_reclen)) {
1678 off += dp->d_reclen;
1679 *cookiep++ = (u_long) off;
1680 }
1681 *ap->a_ncookies = ncookies;
1682 *ap->a_cookies = cookies;
1683 }
1684
1685 /*
1686 * Set the eofflag (NFS uses it)
1687 */
1688 if (ap->a_eofflag)
1689 if (dep->de_FileSize - (uio->uio_offset - bias) <= 0)
1690 *ap->a_eofflag = 1;
1691 else
1692 *ap->a_eofflag = 0;
1693
1694 return error;
1695}
1696
1697static int
1698msdosfs_abortop(ap)
1699 struct vop_abortop_args /* {
1700 struct vnode *a_dvp;
1701 struct componentname *a_cnp;
1702 } */ *ap;
1703{
1704 if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF)
1705 zfree(namei_zone, ap->a_cnp->cn_pnbuf);
1706 return 0;
1707}
1708
1712static int
1713msdosfs_lock(ap)
1714 struct vop_lock_args /* {
1715 struct vnode *a_vp;
1716 int a_flags;
1717 struct proc *a_p;
1718 } */ *ap;
1719{
1720 struct vnode *vp = ap->a_vp;
1721
1722 return (lockmgr(&VTODE(vp)->de_lock, ap->a_flags, &vp->v_interlock,
1723 ap->a_p));
1724}
1725
1726int
1727msdosfs_unlock(ap)
1728 struct vop_unlock_args /* {
1729 struct vnode *a_vp;
1730 int a_flags;
1731 struct proc *a_p;
1732 } */ *ap;
1733{
1734 struct vnode *vp = ap->a_vp;
1735
1736 return (lockmgr(&VTODE(vp)->de_lock, ap->a_flags | LK_RELEASE,
1737 &vp->v_interlock, ap->a_p));
1738}
1739
1740int
1741msdosfs_islocked(ap)
1742 struct vop_islocked_args /* {
1743 struct vnode *a_vp;
1744 } */ *ap;
1745{
1746
1747 return (lockstatus(&VTODE(ap->a_vp)->de_lock));
1748}
1749
1750/*
1751 * vp - address of vnode file the file
1752 * bn - which cluster we are interested in mapping to a filesystem block number.
1753 * vpp - returns the vnode for the block special file holding the filesystem
1754 * containing the file of interest
1755 * bnp - address of where to return the filesystem relative block number
1756 */
1757static int
1758msdosfs_bmap(ap)
1759 struct vop_bmap_args /* {
1760 struct vnode *a_vp;
1761 daddr_t a_bn;
1762 struct vnode **a_vpp;
1763 daddr_t *a_bnp;
1764 int *a_runp;
1765 int *a_runb;
1766 } */ *ap;
1767{
1768 struct denode *dep = VTODE(ap->a_vp);
1769
1770 if (ap->a_vpp != NULL)
1771 *ap->a_vpp = dep->de_devvp;
1772 if (ap->a_bnp == NULL)
1773 return 0;
1774 if (ap->a_runp) {
1775 /*
1776 * Sequential clusters should be counted here.
1777 */
1778 *ap->a_runp = 0;
1779 }
1780 if (ap->a_runb) {
1781 *ap->a_runb = 0;
1782 }
1783 return pcbmap(dep, ap->a_bn, ap->a_bnp, 0);
1784}
1785
1786static int
1787msdosfs_strategy(ap)
1788 struct vop_strategy_args /* {
1789 struct buf *a_bp;
1790 } */ *ap;
1791{
1792 struct buf *bp = ap->a_bp;
1793 struct denode *dep = VTODE(bp->b_vp);
1794 struct vnode *vp;
1795 int error = 0;
1796
1797 if (bp->b_vp->v_type == VBLK || bp->b_vp->v_type == VCHR)
1798 panic("msdosfs_strategy: spec");
1799 /*
1800 * If we don't already know the filesystem relative block number
1801 * then get it using pcbmap(). If pcbmap() returns the block
1802 * number as -1 then we've got a hole in the file. DOS filesystems
1803 * don't allow files with holes, so we shouldn't ever see this.
1804 */
1805 if (bp->b_blkno == bp->b_lblkno) {
1806 error = pcbmap(dep, bp->b_lblkno, &bp->b_blkno, 0);
1807 if (error)
1808 bp->b_blkno = -1;
1809 if (bp->b_blkno == -1)
1810 clrbuf(bp);
1811 }
1812 if (bp->b_blkno == -1) {
1813 biodone(bp);
1814 return error;
1815 }
1816#ifdef DIAGNOSTIC
1817#endif
1818 /*
1819 * Read/write the block from/to the disk that contains the desired
1820 * file block.
1821 */
1822 vp = dep->de_devvp;
1823 bp->b_dev = vp->v_rdev;
1824 VOCALL(vp->v_op, VOFFSET(vop_strategy), ap);
1825 return 0;
1826}
1827
1828static int
1829msdosfs_print(ap)
1830 struct vop_print_args /* {
1831 struct vnode *vp;
1832 } */ *ap;
1833{
1834 struct denode *dep = VTODE(ap->a_vp);
1835
1836 printf(
1837 "tag VT_MSDOSFS, startcluster %d, dircluster %ld, diroffset %ld ",
1838 dep->de_StartCluster, dep->de_dirclust, dep->de_diroffset);
1839 printf(" dev %d, %d", major(dep->de_dev), minor(dep->de_dev));
1840 lockmgr_printinfo(&dep->de_lock);
1841 printf("\n");
1842 return 0;
1843}
1844
1845static int
1846msdosfs_pathconf(ap)
1847 struct vop_pathconf_args /* {
1848 struct vnode *a_vp;
1849 int a_name;
1850 int *a_retval;
1851 } */ *ap;
1852{
1853 switch (ap->a_name) {
1854 case _PC_LINK_MAX:
1855 *ap->a_retval = 1;
1856 return 0;
1857 case _PC_NAME_MAX:
1858 *ap->a_retval = 12;
1859 return 0;
1860 case _PC_PATH_MAX:
1861 *ap->a_retval = PATH_MAX; /* 255? */
1862 return 0;
1863 case _PC_CHOWN_RESTRICTED:
1864 *ap->a_retval = 1;
1865 return 0;
1866 case _PC_NO_TRUNC:
1867 *ap->a_retval = 0;
1868 return 0;
1869 default:
1870 return EINVAL;
1871 }
1872}
1873
1874/* Global vfs data structures for msdosfs */
1875vop_t **msdosfs_vnodeop_p;
1876static struct vnodeopv_entry_desc msdosfs_vnodeop_entries[] = {
1877 { &vop_default_desc, (vop_t *) vn_default_error },
1878 { &vop_abortop_desc, (vop_t *) msdosfs_abortop },
1879 { &vop_access_desc, (vop_t *) msdosfs_access },
1880 { &vop_bmap_desc, (vop_t *) msdosfs_bmap },
1881 { &vop_cachedlookup_desc, (vop_t *) msdosfs_lookup },
1882 { &vop_close_desc, (vop_t *) msdosfs_close },
1883 { &vop_create_desc, (vop_t *) msdosfs_create },
1884 { &vop_fsync_desc, (vop_t *) msdosfs_fsync },
1885 { &vop_getattr_desc, (vop_t *) msdosfs_getattr },
1886 { &vop_inactive_desc, (vop_t *) msdosfs_inactive },
1709/*
1710 * vp - address of vnode file the file
1711 * bn - which cluster we are interested in mapping to a filesystem block number.
1712 * vpp - returns the vnode for the block special file holding the filesystem
1713 * containing the file of interest
1714 * bnp - address of where to return the filesystem relative block number
1715 */
1716static int
1717msdosfs_bmap(ap)
1718 struct vop_bmap_args /* {
1719 struct vnode *a_vp;
1720 daddr_t a_bn;
1721 struct vnode **a_vpp;
1722 daddr_t *a_bnp;
1723 int *a_runp;
1724 int *a_runb;
1725 } */ *ap;
1726{
1727 struct denode *dep = VTODE(ap->a_vp);
1728
1729 if (ap->a_vpp != NULL)
1730 *ap->a_vpp = dep->de_devvp;
1731 if (ap->a_bnp == NULL)
1732 return 0;
1733 if (ap->a_runp) {
1734 /*
1735 * Sequential clusters should be counted here.
1736 */
1737 *ap->a_runp = 0;
1738 }
1739 if (ap->a_runb) {
1740 *ap->a_runb = 0;
1741 }
1742 return pcbmap(dep, ap->a_bn, ap->a_bnp, 0);
1743}
1744
1745static int
1746msdosfs_strategy(ap)
1747 struct vop_strategy_args /* {
1748 struct buf *a_bp;
1749 } */ *ap;
1750{
1751 struct buf *bp = ap->a_bp;
1752 struct denode *dep = VTODE(bp->b_vp);
1753 struct vnode *vp;
1754 int error = 0;
1755
1756 if (bp->b_vp->v_type == VBLK || bp->b_vp->v_type == VCHR)
1757 panic("msdosfs_strategy: spec");
1758 /*
1759 * If we don't already know the filesystem relative block number
1760 * then get it using pcbmap(). If pcbmap() returns the block
1761 * number as -1 then we've got a hole in the file. DOS filesystems
1762 * don't allow files with holes, so we shouldn't ever see this.
1763 */
1764 if (bp->b_blkno == bp->b_lblkno) {
1765 error = pcbmap(dep, bp->b_lblkno, &bp->b_blkno, 0);
1766 if (error)
1767 bp->b_blkno = -1;
1768 if (bp->b_blkno == -1)
1769 clrbuf(bp);
1770 }
1771 if (bp->b_blkno == -1) {
1772 biodone(bp);
1773 return error;
1774 }
1775#ifdef DIAGNOSTIC
1776#endif
1777 /*
1778 * Read/write the block from/to the disk that contains the desired
1779 * file block.
1780 */
1781 vp = dep->de_devvp;
1782 bp->b_dev = vp->v_rdev;
1783 VOCALL(vp->v_op, VOFFSET(vop_strategy), ap);
1784 return 0;
1785}
1786
1787static int
1788msdosfs_print(ap)
1789 struct vop_print_args /* {
1790 struct vnode *vp;
1791 } */ *ap;
1792{
1793 struct denode *dep = VTODE(ap->a_vp);
1794
1795 printf(
1796 "tag VT_MSDOSFS, startcluster %d, dircluster %ld, diroffset %ld ",
1797 dep->de_StartCluster, dep->de_dirclust, dep->de_diroffset);
1798 printf(" dev %d, %d", major(dep->de_dev), minor(dep->de_dev));
1799 lockmgr_printinfo(&dep->de_lock);
1800 printf("\n");
1801 return 0;
1802}
1803
1804static int
1805msdosfs_pathconf(ap)
1806 struct vop_pathconf_args /* {
1807 struct vnode *a_vp;
1808 int a_name;
1809 int *a_retval;
1810 } */ *ap;
1811{
1812 switch (ap->a_name) {
1813 case _PC_LINK_MAX:
1814 *ap->a_retval = 1;
1815 return 0;
1816 case _PC_NAME_MAX:
1817 *ap->a_retval = 12;
1818 return 0;
1819 case _PC_PATH_MAX:
1820 *ap->a_retval = PATH_MAX; /* 255? */
1821 return 0;
1822 case _PC_CHOWN_RESTRICTED:
1823 *ap->a_retval = 1;
1824 return 0;
1825 case _PC_NO_TRUNC:
1826 *ap->a_retval = 0;
1827 return 0;
1828 default:
1829 return EINVAL;
1830 }
1831}
1832
1833/* Global vfs data structures for msdosfs */
1834vop_t **msdosfs_vnodeop_p;
1835static struct vnodeopv_entry_desc msdosfs_vnodeop_entries[] = {
1836 { &vop_default_desc, (vop_t *) vn_default_error },
1837 { &vop_abortop_desc, (vop_t *) msdosfs_abortop },
1838 { &vop_access_desc, (vop_t *) msdosfs_access },
1839 { &vop_bmap_desc, (vop_t *) msdosfs_bmap },
1840 { &vop_cachedlookup_desc, (vop_t *) msdosfs_lookup },
1841 { &vop_close_desc, (vop_t *) msdosfs_close },
1842 { &vop_create_desc, (vop_t *) msdosfs_create },
1843 { &vop_fsync_desc, (vop_t *) msdosfs_fsync },
1844 { &vop_getattr_desc, (vop_t *) msdosfs_getattr },
1845 { &vop_inactive_desc, (vop_t *) msdosfs_inactive },
1887 { &vop_islocked_desc, (vop_t *) msdosfs_islocked },
1846 { &vop_islocked_desc, (vop_t *) vop_stdislocked },
1888 { &vop_link_desc, (vop_t *) msdosfs_link },
1847 { &vop_link_desc, (vop_t *) msdosfs_link },
1889 { &vop_lock_desc, (vop_t *) msdosfs_lock },
1848 { &vop_lock_desc, (vop_t *) vop_stdlock },
1890 { &vop_lookup_desc, (vop_t *) vfs_cache_lookup },
1891 { &vop_mkdir_desc, (vop_t *) msdosfs_mkdir },
1892 { &vop_mknod_desc, (vop_t *) msdosfs_mknod },
1893 { &vop_pathconf_desc, (vop_t *) msdosfs_pathconf },
1894 { &vop_print_desc, (vop_t *) msdosfs_print },
1895 { &vop_read_desc, (vop_t *) msdosfs_read },
1896 { &vop_readdir_desc, (vop_t *) msdosfs_readdir },
1897 { &vop_reclaim_desc, (vop_t *) msdosfs_reclaim },
1898 { &vop_remove_desc, (vop_t *) msdosfs_remove },
1899 { &vop_rename_desc, (vop_t *) msdosfs_rename },
1900 { &vop_rmdir_desc, (vop_t *) msdosfs_rmdir },
1901 { &vop_setattr_desc, (vop_t *) msdosfs_setattr },
1902 { &vop_strategy_desc, (vop_t *) msdosfs_strategy },
1903 { &vop_symlink_desc, (vop_t *) msdosfs_symlink },
1849 { &vop_lookup_desc, (vop_t *) vfs_cache_lookup },
1850 { &vop_mkdir_desc, (vop_t *) msdosfs_mkdir },
1851 { &vop_mknod_desc, (vop_t *) msdosfs_mknod },
1852 { &vop_pathconf_desc, (vop_t *) msdosfs_pathconf },
1853 { &vop_print_desc, (vop_t *) msdosfs_print },
1854 { &vop_read_desc, (vop_t *) msdosfs_read },
1855 { &vop_readdir_desc, (vop_t *) msdosfs_readdir },
1856 { &vop_reclaim_desc, (vop_t *) msdosfs_reclaim },
1857 { &vop_remove_desc, (vop_t *) msdosfs_remove },
1858 { &vop_rename_desc, (vop_t *) msdosfs_rename },
1859 { &vop_rmdir_desc, (vop_t *) msdosfs_rmdir },
1860 { &vop_setattr_desc, (vop_t *) msdosfs_setattr },
1861 { &vop_strategy_desc, (vop_t *) msdosfs_strategy },
1862 { &vop_symlink_desc, (vop_t *) msdosfs_symlink },
1904 { &vop_unlock_desc, (vop_t *) msdosfs_unlock },
1863 { &vop_unlock_desc, (vop_t *) vop_stdunlock },
1905 { &vop_write_desc, (vop_t *) msdosfs_write },
1906 { NULL, NULL }
1907};
1908static struct vnodeopv_desc msdosfs_vnodeop_opv_desc =
1909 { &msdosfs_vnodeop_p, msdosfs_vnodeop_entries };
1910
1911VNODEOP_SET(msdosfs_vnodeop_opv_desc);
1864 { &vop_write_desc, (vop_t *) msdosfs_write },
1865 { NULL, NULL }
1866};
1867static struct vnodeopv_desc msdosfs_vnodeop_opv_desc =
1868 { &msdosfs_vnodeop_p, msdosfs_vnodeop_entries };
1869
1870VNODEOP_SET(msdosfs_vnodeop_opv_desc);