1*0a6a1f1dSLionel Sambuc /* $NetBSD: msdosfs_vnops.c,v 1.93 2015/04/04 12:34:44 riastradh Exp $ */
284d9c625SLionel Sambuc
384d9c625SLionel Sambuc /*-
484d9c625SLionel Sambuc * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
584d9c625SLionel Sambuc * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
684d9c625SLionel Sambuc * All rights reserved.
784d9c625SLionel Sambuc * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
884d9c625SLionel Sambuc *
984d9c625SLionel Sambuc * Redistribution and use in source and binary forms, with or without
1084d9c625SLionel Sambuc * modification, are permitted provided that the following conditions
1184d9c625SLionel Sambuc * are met:
1284d9c625SLionel Sambuc * 1. Redistributions of source code must retain the above copyright
1384d9c625SLionel Sambuc * notice, this list of conditions and the following disclaimer.
1484d9c625SLionel Sambuc * 2. Redistributions in binary form must reproduce the above copyright
1584d9c625SLionel Sambuc * notice, this list of conditions and the following disclaimer in the
1684d9c625SLionel Sambuc * documentation and/or other materials provided with the distribution.
1784d9c625SLionel Sambuc * 3. All advertising materials mentioning features or use of this software
1884d9c625SLionel Sambuc * must display the following acknowledgement:
1984d9c625SLionel Sambuc * This product includes software developed by TooLs GmbH.
2084d9c625SLionel Sambuc * 4. The name of TooLs GmbH may not be used to endorse or promote products
2184d9c625SLionel Sambuc * derived from this software without specific prior written permission.
2284d9c625SLionel Sambuc *
2384d9c625SLionel Sambuc * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
2484d9c625SLionel Sambuc * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2584d9c625SLionel Sambuc * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2684d9c625SLionel Sambuc * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2784d9c625SLionel Sambuc * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
2884d9c625SLionel Sambuc * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
2984d9c625SLionel Sambuc * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
3084d9c625SLionel Sambuc * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
3184d9c625SLionel Sambuc * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
3284d9c625SLionel Sambuc * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3384d9c625SLionel Sambuc */
3484d9c625SLionel Sambuc /*
3584d9c625SLionel Sambuc * Written by Paul Popelka (paulp@uts.amdahl.com)
3684d9c625SLionel Sambuc *
3784d9c625SLionel Sambuc * You can do anything you want with this software, just don't say you wrote
3884d9c625SLionel Sambuc * it, and don't remove this notice.
3984d9c625SLionel Sambuc *
4084d9c625SLionel Sambuc * This software is provided "as is".
4184d9c625SLionel Sambuc *
4284d9c625SLionel Sambuc * The author supplies this software to be publicly redistributed on the
4384d9c625SLionel Sambuc * understanding that the author is not responsible for the correct
4484d9c625SLionel Sambuc * functioning of this software in any circumstances and is not liable for
4584d9c625SLionel Sambuc * any damages caused by this software.
4684d9c625SLionel Sambuc *
4784d9c625SLionel Sambuc * October 1992
4884d9c625SLionel Sambuc */
4984d9c625SLionel Sambuc
5084d9c625SLionel Sambuc #include <sys/cdefs.h>
51*0a6a1f1dSLionel Sambuc __KERNEL_RCSID(0, "$NetBSD: msdosfs_vnops.c,v 1.93 2015/04/04 12:34:44 riastradh Exp $");
5284d9c625SLionel Sambuc
5384d9c625SLionel Sambuc #include <sys/param.h>
5484d9c625SLionel Sambuc #include <sys/systm.h>
5584d9c625SLionel Sambuc #include <sys/namei.h>
5684d9c625SLionel Sambuc #include <sys/resourcevar.h> /* defines plimit structure in proc struct */
5784d9c625SLionel Sambuc #include <sys/kernel.h>
5884d9c625SLionel Sambuc #include <sys/file.h> /* define FWRITE ... */
5984d9c625SLionel Sambuc #include <sys/stat.h>
6084d9c625SLionel Sambuc #include <sys/buf.h>
6184d9c625SLionel Sambuc #include <sys/proc.h>
6284d9c625SLionel Sambuc #include <sys/mount.h>
6384d9c625SLionel Sambuc #include <sys/fstrans.h>
6484d9c625SLionel Sambuc #include <sys/vnode.h>
6584d9c625SLionel Sambuc #include <sys/signalvar.h>
6684d9c625SLionel Sambuc #include <sys/malloc.h>
6784d9c625SLionel Sambuc #include <sys/dirent.h>
6884d9c625SLionel Sambuc #include <sys/lockf.h>
6984d9c625SLionel Sambuc #include <sys/kauth.h>
7084d9c625SLionel Sambuc
7184d9c625SLionel Sambuc #include <miscfs/genfs/genfs.h>
7284d9c625SLionel Sambuc #include <miscfs/specfs/specdev.h> /* XXX */ /* defines v_rdev */
7384d9c625SLionel Sambuc
7484d9c625SLionel Sambuc #include <uvm/uvm_extern.h>
7584d9c625SLionel Sambuc
7684d9c625SLionel Sambuc #include <fs/msdosfs/bpb.h>
7784d9c625SLionel Sambuc #include <fs/msdosfs/direntry.h>
7884d9c625SLionel Sambuc #include <fs/msdosfs/denode.h>
7984d9c625SLionel Sambuc #include <fs/msdosfs/msdosfsmount.h>
8084d9c625SLionel Sambuc #include <fs/msdosfs/fat.h>
8184d9c625SLionel Sambuc
8284d9c625SLionel Sambuc /*
8384d9c625SLionel Sambuc * Some general notes:
8484d9c625SLionel Sambuc *
8584d9c625SLionel Sambuc * In the ufs filesystem the inodes, superblocks, and indirect blocks are
8684d9c625SLionel Sambuc * read/written using the vnode for the filesystem. Blocks that represent
8784d9c625SLionel Sambuc * the contents of a file are read/written using the vnode for the file
8884d9c625SLionel Sambuc * (including directories when they are read/written as files). This
8984d9c625SLionel Sambuc * presents problems for the dos filesystem because data that should be in
9084d9c625SLionel Sambuc * an inode (if dos had them) resides in the directory itself. Since we
9184d9c625SLionel Sambuc * must update directory entries without the benefit of having the vnode
9284d9c625SLionel Sambuc * for the directory we must use the vnode for the filesystem. This means
9384d9c625SLionel Sambuc * that when a directory is actually read/written (via read, write, or
9484d9c625SLionel Sambuc * readdir, or seek) we must use the vnode for the filesystem instead of
9584d9c625SLionel Sambuc * the vnode for the directory as would happen in ufs. This is to insure we
9684d9c625SLionel Sambuc * retrieve the correct block from the buffer cache since the hash value is
9784d9c625SLionel Sambuc * based upon the vnode address and the desired block number.
9884d9c625SLionel Sambuc */
9984d9c625SLionel Sambuc
10084d9c625SLionel Sambuc /*
10184d9c625SLionel Sambuc * Create a regular file. On entry the directory to contain the file being
10284d9c625SLionel Sambuc * created is locked. We must release before we return.
10384d9c625SLionel Sambuc */
10484d9c625SLionel Sambuc int
msdosfs_create(void * v)10584d9c625SLionel Sambuc msdosfs_create(void *v)
10684d9c625SLionel Sambuc {
107*0a6a1f1dSLionel Sambuc struct vop_create_v3_args /* {
10884d9c625SLionel Sambuc struct vnode *a_dvp;
10984d9c625SLionel Sambuc struct vnode **a_vpp;
11084d9c625SLionel Sambuc struct componentname *a_cnp;
11184d9c625SLionel Sambuc struct vattr *a_vap;
11284d9c625SLionel Sambuc } */ *ap = v;
11384d9c625SLionel Sambuc struct componentname *cnp = ap->a_cnp;
11484d9c625SLionel Sambuc struct denode ndirent;
11584d9c625SLionel Sambuc struct denode *dep;
11684d9c625SLionel Sambuc struct denode *pdep = VTODE(ap->a_dvp);
11784d9c625SLionel Sambuc int error;
11884d9c625SLionel Sambuc
11984d9c625SLionel Sambuc #ifdef MSDOSFS_DEBUG
12084d9c625SLionel Sambuc printf("msdosfs_create(cnp %p, vap %p\n", cnp, ap->a_vap);
12184d9c625SLionel Sambuc #endif
12284d9c625SLionel Sambuc
12384d9c625SLionel Sambuc fstrans_start(ap->a_dvp->v_mount, FSTRANS_SHARED);
12484d9c625SLionel Sambuc /*
12584d9c625SLionel Sambuc * If this is the root directory and there is no space left we
12684d9c625SLionel Sambuc * can't do anything. This is because the root directory can not
12784d9c625SLionel Sambuc * change size.
12884d9c625SLionel Sambuc */
12984d9c625SLionel Sambuc if (pdep->de_StartCluster == MSDOSFSROOT
13084d9c625SLionel Sambuc && pdep->de_fndoffset >= pdep->de_FileSize) {
13184d9c625SLionel Sambuc error = ENOSPC;
13284d9c625SLionel Sambuc goto bad;
13384d9c625SLionel Sambuc }
13484d9c625SLionel Sambuc
13584d9c625SLionel Sambuc /*
13684d9c625SLionel Sambuc * Create a directory entry for the file, then call createde() to
13784d9c625SLionel Sambuc * have it installed. NOTE: DOS files are always executable. We
13884d9c625SLionel Sambuc * use the absence of the owner write bit to make the file
13984d9c625SLionel Sambuc * readonly.
14084d9c625SLionel Sambuc */
14184d9c625SLionel Sambuc memset(&ndirent, 0, sizeof(ndirent));
14284d9c625SLionel Sambuc if ((error = uniqdosname(pdep, cnp, ndirent.de_Name)) != 0)
14384d9c625SLionel Sambuc goto bad;
14484d9c625SLionel Sambuc
14584d9c625SLionel Sambuc ndirent.de_Attributes = (ap->a_vap->va_mode & S_IWUSR) ?
14684d9c625SLionel Sambuc ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY;
14784d9c625SLionel Sambuc ndirent.de_StartCluster = 0;
14884d9c625SLionel Sambuc ndirent.de_FileSize = 0;
14984d9c625SLionel Sambuc ndirent.de_dev = pdep->de_dev;
15084d9c625SLionel Sambuc ndirent.de_devvp = pdep->de_devvp;
15184d9c625SLionel Sambuc ndirent.de_pmp = pdep->de_pmp;
15284d9c625SLionel Sambuc ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
15384d9c625SLionel Sambuc DETIMES(&ndirent, NULL, NULL, NULL, pdep->de_pmp->pm_gmtoff);
15484d9c625SLionel Sambuc if ((error = createde(&ndirent, pdep, &dep, cnp)) != 0)
15584d9c625SLionel Sambuc goto bad;
15684d9c625SLionel Sambuc fstrans_done(ap->a_dvp->v_mount);
15784d9c625SLionel Sambuc VN_KNOTE(ap->a_dvp, NOTE_WRITE);
15884d9c625SLionel Sambuc *ap->a_vpp = DETOV(dep);
15984d9c625SLionel Sambuc return (0);
16084d9c625SLionel Sambuc
16184d9c625SLionel Sambuc bad:
16284d9c625SLionel Sambuc fstrans_done(ap->a_dvp->v_mount);
16384d9c625SLionel Sambuc return (error);
16484d9c625SLionel Sambuc }
16584d9c625SLionel Sambuc
16684d9c625SLionel Sambuc int
msdosfs_close(void * v)16784d9c625SLionel Sambuc msdosfs_close(void *v)
16884d9c625SLionel Sambuc {
16984d9c625SLionel Sambuc struct vop_close_args /* {
17084d9c625SLionel Sambuc struct vnode *a_vp;
17184d9c625SLionel Sambuc int a_fflag;
17284d9c625SLionel Sambuc kauth_cred_t a_cred;
17384d9c625SLionel Sambuc } */ *ap = v;
17484d9c625SLionel Sambuc struct vnode *vp = ap->a_vp;
17584d9c625SLionel Sambuc struct denode *dep = VTODE(vp);
17684d9c625SLionel Sambuc
17784d9c625SLionel Sambuc fstrans_start(vp->v_mount, FSTRANS_SHARED);
17884d9c625SLionel Sambuc mutex_enter(vp->v_interlock);
17984d9c625SLionel Sambuc if (vp->v_usecount > 1)
18084d9c625SLionel Sambuc DETIMES(dep, NULL, NULL, NULL, dep->de_pmp->pm_gmtoff);
18184d9c625SLionel Sambuc mutex_exit(vp->v_interlock);
18284d9c625SLionel Sambuc fstrans_done(vp->v_mount);
18384d9c625SLionel Sambuc return (0);
18484d9c625SLionel Sambuc }
18584d9c625SLionel Sambuc
18684d9c625SLionel Sambuc static int
msdosfs_check_possible(struct vnode * vp,struct denode * dep,mode_t mode)18784d9c625SLionel Sambuc msdosfs_check_possible(struct vnode *vp, struct denode *dep, mode_t mode)
18884d9c625SLionel Sambuc {
18984d9c625SLionel Sambuc
19084d9c625SLionel Sambuc /*
19184d9c625SLionel Sambuc * Disallow write attempts on read-only file systems;
19284d9c625SLionel Sambuc * unless the file is a socket, fifo, or a block or
19384d9c625SLionel Sambuc * character device resident on the file system.
19484d9c625SLionel Sambuc */
19584d9c625SLionel Sambuc if (mode & VWRITE) {
19684d9c625SLionel Sambuc switch (vp->v_type) {
19784d9c625SLionel Sambuc case VDIR:
19884d9c625SLionel Sambuc case VLNK:
19984d9c625SLionel Sambuc case VREG:
20084d9c625SLionel Sambuc if (vp->v_mount->mnt_flag & MNT_RDONLY)
20184d9c625SLionel Sambuc return (EROFS);
20284d9c625SLionel Sambuc default:
20384d9c625SLionel Sambuc break;
20484d9c625SLionel Sambuc }
20584d9c625SLionel Sambuc }
20684d9c625SLionel Sambuc
20784d9c625SLionel Sambuc return 0;
20884d9c625SLionel Sambuc }
20984d9c625SLionel Sambuc
21084d9c625SLionel Sambuc static int
msdosfs_check_permitted(struct vnode * vp,struct denode * dep,mode_t mode,kauth_cred_t cred)21184d9c625SLionel Sambuc msdosfs_check_permitted(struct vnode *vp, struct denode *dep, mode_t mode,
21284d9c625SLionel Sambuc kauth_cred_t cred)
21384d9c625SLionel Sambuc {
21484d9c625SLionel Sambuc struct msdosfsmount *pmp = dep->de_pmp;
21584d9c625SLionel Sambuc mode_t file_mode;
21684d9c625SLionel Sambuc
21784d9c625SLionel Sambuc if ((dep->de_Attributes & ATTR_READONLY) == 0)
21884d9c625SLionel Sambuc file_mode = S_IRWXU|S_IRWXG|S_IRWXO;
21984d9c625SLionel Sambuc else
22084d9c625SLionel Sambuc file_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
22184d9c625SLionel Sambuc
22284d9c625SLionel Sambuc file_mode &= (vp->v_type == VDIR ? pmp->pm_dirmask : pmp->pm_mask);
22384d9c625SLionel Sambuc
22484d9c625SLionel Sambuc return kauth_authorize_vnode(cred, KAUTH_ACCESS_ACTION(mode,
22584d9c625SLionel Sambuc vp->v_type, file_mode), vp, NULL, genfs_can_access(vp->v_type,
22684d9c625SLionel Sambuc file_mode, pmp->pm_uid, pmp->pm_gid, mode, cred));
22784d9c625SLionel Sambuc }
22884d9c625SLionel Sambuc
22984d9c625SLionel Sambuc int
msdosfs_access(void * v)23084d9c625SLionel Sambuc msdosfs_access(void *v)
23184d9c625SLionel Sambuc {
23284d9c625SLionel Sambuc struct vop_access_args /* {
23384d9c625SLionel Sambuc struct vnode *a_vp;
23484d9c625SLionel Sambuc int a_mode;
23584d9c625SLionel Sambuc kauth_cred_t a_cred;
23684d9c625SLionel Sambuc } */ *ap = v;
23784d9c625SLionel Sambuc struct vnode *vp = ap->a_vp;
23884d9c625SLionel Sambuc struct denode *dep = VTODE(vp);
23984d9c625SLionel Sambuc int error;
24084d9c625SLionel Sambuc
24184d9c625SLionel Sambuc error = msdosfs_check_possible(vp, dep, ap->a_mode);
24284d9c625SLionel Sambuc if (error)
24384d9c625SLionel Sambuc return error;
24484d9c625SLionel Sambuc
24584d9c625SLionel Sambuc error = msdosfs_check_permitted(vp, dep, ap->a_mode, ap->a_cred);
24684d9c625SLionel Sambuc
24784d9c625SLionel Sambuc return error;
24884d9c625SLionel Sambuc }
24984d9c625SLionel Sambuc
25084d9c625SLionel Sambuc int
msdosfs_getattr(void * v)25184d9c625SLionel Sambuc msdosfs_getattr(void *v)
25284d9c625SLionel Sambuc {
25384d9c625SLionel Sambuc struct vop_getattr_args /* {
25484d9c625SLionel Sambuc struct vnode *a_vp;
25584d9c625SLionel Sambuc struct vattr *a_vap;
25684d9c625SLionel Sambuc kauth_cred_t a_cred;
25784d9c625SLionel Sambuc } */ *ap = v;
25884d9c625SLionel Sambuc struct denode *dep = VTODE(ap->a_vp);
25984d9c625SLionel Sambuc struct msdosfsmount *pmp = dep->de_pmp;
26084d9c625SLionel Sambuc struct vattr *vap = ap->a_vap;
26184d9c625SLionel Sambuc mode_t mode;
26284d9c625SLionel Sambuc u_long dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry);
26384d9c625SLionel Sambuc ino_t fileid;
26484d9c625SLionel Sambuc
26584d9c625SLionel Sambuc fstrans_start(ap->a_vp->v_mount, FSTRANS_SHARED);
26684d9c625SLionel Sambuc DETIMES(dep, NULL, NULL, NULL, pmp->pm_gmtoff);
26784d9c625SLionel Sambuc vap->va_fsid = dep->de_dev;
26884d9c625SLionel Sambuc /*
26984d9c625SLionel Sambuc * The following computation of the fileid must be the same as that
27084d9c625SLionel Sambuc * used in msdosfs_readdir() to compute d_fileno. If not, pwd
27184d9c625SLionel Sambuc * doesn't work.
27284d9c625SLionel Sambuc */
27384d9c625SLionel Sambuc if (dep->de_Attributes & ATTR_DIRECTORY) {
27484d9c625SLionel Sambuc fileid = cntobn(pmp, (ino_t)dep->de_StartCluster) * dirsperblk;
27584d9c625SLionel Sambuc if (dep->de_StartCluster == MSDOSFSROOT)
27684d9c625SLionel Sambuc fileid = 1;
27784d9c625SLionel Sambuc } else {
27884d9c625SLionel Sambuc fileid = cntobn(pmp, (ino_t)dep->de_dirclust) * dirsperblk;
27984d9c625SLionel Sambuc if (dep->de_dirclust == MSDOSFSROOT)
28084d9c625SLionel Sambuc fileid = roottobn(pmp, 0) * dirsperblk;
28184d9c625SLionel Sambuc fileid += dep->de_diroffset / sizeof(struct direntry);
28284d9c625SLionel Sambuc }
28384d9c625SLionel Sambuc vap->va_fileid = fileid;
28484d9c625SLionel Sambuc if ((dep->de_Attributes & ATTR_READONLY) == 0)
28584d9c625SLionel Sambuc mode = S_IRWXU|S_IRWXG|S_IRWXO;
28684d9c625SLionel Sambuc else
28784d9c625SLionel Sambuc mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
28884d9c625SLionel Sambuc vap->va_mode =
28984d9c625SLionel Sambuc mode & (ap->a_vp->v_type == VDIR ? pmp->pm_dirmask : pmp->pm_mask);
29084d9c625SLionel Sambuc vap->va_uid = pmp->pm_uid;
29184d9c625SLionel Sambuc vap->va_gid = pmp->pm_gid;
29284d9c625SLionel Sambuc vap->va_nlink = 1;
29384d9c625SLionel Sambuc vap->va_rdev = 0;
29484d9c625SLionel Sambuc vap->va_size = ap->a_vp->v_size;
29584d9c625SLionel Sambuc dos2unixtime(dep->de_MDate, dep->de_MTime, 0, pmp->pm_gmtoff,
29684d9c625SLionel Sambuc &vap->va_mtime);
29784d9c625SLionel Sambuc if (dep->de_pmp->pm_flags & MSDOSFSMNT_LONGNAME) {
29884d9c625SLionel Sambuc dos2unixtime(dep->de_ADate, 0, 0, pmp->pm_gmtoff,
29984d9c625SLionel Sambuc &vap->va_atime);
30084d9c625SLionel Sambuc dos2unixtime(dep->de_CDate, dep->de_CTime, dep->de_CHun,
30184d9c625SLionel Sambuc pmp->pm_gmtoff, &vap->va_ctime);
30284d9c625SLionel Sambuc } else {
30384d9c625SLionel Sambuc vap->va_atime = vap->va_mtime;
30484d9c625SLionel Sambuc vap->va_ctime = vap->va_mtime;
30584d9c625SLionel Sambuc }
30684d9c625SLionel Sambuc vap->va_flags = 0;
30784d9c625SLionel Sambuc if ((dep->de_Attributes & ATTR_ARCHIVE) == 0) {
30884d9c625SLionel Sambuc vap->va_flags |= SF_ARCHIVED;
30984d9c625SLionel Sambuc vap->va_mode |= S_ARCH1;
31084d9c625SLionel Sambuc }
31184d9c625SLionel Sambuc vap->va_gen = 0;
31284d9c625SLionel Sambuc vap->va_blocksize = pmp->pm_bpcluster;
31384d9c625SLionel Sambuc vap->va_bytes =
31484d9c625SLionel Sambuc (dep->de_FileSize + pmp->pm_crbomask) & ~pmp->pm_crbomask;
31584d9c625SLionel Sambuc vap->va_type = ap->a_vp->v_type;
31684d9c625SLionel Sambuc fstrans_done(ap->a_vp->v_mount);
31784d9c625SLionel Sambuc return (0);
31884d9c625SLionel Sambuc }
31984d9c625SLionel Sambuc
32084d9c625SLionel Sambuc int
msdosfs_setattr(void * v)32184d9c625SLionel Sambuc msdosfs_setattr(void *v)
32284d9c625SLionel Sambuc {
32384d9c625SLionel Sambuc struct vop_setattr_args /* {
32484d9c625SLionel Sambuc struct vnode *a_vp;
32584d9c625SLionel Sambuc struct vattr *a_vap;
32684d9c625SLionel Sambuc kauth_cred_t a_cred;
32784d9c625SLionel Sambuc } */ *ap = v;
32884d9c625SLionel Sambuc int error = 0, de_changed = 0;
32984d9c625SLionel Sambuc struct denode *dep = VTODE(ap->a_vp);
33084d9c625SLionel Sambuc struct msdosfsmount *pmp = dep->de_pmp;
33184d9c625SLionel Sambuc struct vnode *vp = ap->a_vp;
33284d9c625SLionel Sambuc struct vattr *vap = ap->a_vap;
33384d9c625SLionel Sambuc kauth_cred_t cred = ap->a_cred;
33484d9c625SLionel Sambuc
33584d9c625SLionel Sambuc #ifdef MSDOSFS_DEBUG
33684d9c625SLionel Sambuc printf("msdosfs_setattr(): vp %p, vap %p, cred %p\n",
33784d9c625SLionel Sambuc ap->a_vp, vap, cred);
33884d9c625SLionel Sambuc #endif
33984d9c625SLionel Sambuc /*
34084d9c625SLionel Sambuc * Note we silently ignore uid or gid changes.
34184d9c625SLionel Sambuc */
34284d9c625SLionel Sambuc if ((vap->va_type != VNON) || (vap->va_nlink != (nlink_t)VNOVAL) ||
34384d9c625SLionel Sambuc (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
34484d9c625SLionel Sambuc (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
34584d9c625SLionel Sambuc (vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL) ||
34684d9c625SLionel Sambuc (vap->va_uid != VNOVAL && vap->va_uid != pmp->pm_uid) ||
34784d9c625SLionel Sambuc (vap->va_gid != VNOVAL && vap->va_gid != pmp->pm_gid)) {
34884d9c625SLionel Sambuc #ifdef MSDOSFS_DEBUG
34984d9c625SLionel Sambuc printf("msdosfs_setattr(): returning EINVAL\n");
35084d9c625SLionel Sambuc printf(" va_type %d, va_nlink %x, va_fsid %"PRIx64", va_fileid %llx\n",
35184d9c625SLionel Sambuc vap->va_type, vap->va_nlink, vap->va_fsid,
35284d9c625SLionel Sambuc (unsigned long long)vap->va_fileid);
35384d9c625SLionel Sambuc printf(" va_blocksize %lx, va_rdev %"PRIx64", va_bytes %"PRIx64", va_gen %lx\n",
35484d9c625SLionel Sambuc vap->va_blocksize, vap->va_rdev, vap->va_bytes, vap->va_gen);
35584d9c625SLionel Sambuc #endif
35684d9c625SLionel Sambuc return (EINVAL);
35784d9c625SLionel Sambuc }
35884d9c625SLionel Sambuc /*
35984d9c625SLionel Sambuc * Silently ignore attributes modifications on directories.
36084d9c625SLionel Sambuc */
36184d9c625SLionel Sambuc if (ap->a_vp->v_type == VDIR)
36284d9c625SLionel Sambuc return 0;
36384d9c625SLionel Sambuc
36484d9c625SLionel Sambuc fstrans_start(vp->v_mount, FSTRANS_SHARED);
36584d9c625SLionel Sambuc if (vap->va_size != VNOVAL) {
36684d9c625SLionel Sambuc if (vp->v_mount->mnt_flag & MNT_RDONLY) {
36784d9c625SLionel Sambuc error = EROFS;
36884d9c625SLionel Sambuc goto bad;
36984d9c625SLionel Sambuc }
37084d9c625SLionel Sambuc error = detrunc(dep, (u_long)vap->va_size, 0, cred);
37184d9c625SLionel Sambuc if (error)
37284d9c625SLionel Sambuc goto bad;
37384d9c625SLionel Sambuc de_changed = 1;
37484d9c625SLionel Sambuc }
37584d9c625SLionel Sambuc if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
37684d9c625SLionel Sambuc if (vp->v_mount->mnt_flag & MNT_RDONLY) {
37784d9c625SLionel Sambuc error = EROFS;
37884d9c625SLionel Sambuc goto bad;
37984d9c625SLionel Sambuc }
38084d9c625SLionel Sambuc error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_TIMES,
38184d9c625SLionel Sambuc ap->a_vp, NULL, genfs_can_chtimes(ap->a_vp, vap->va_vaflags,
38284d9c625SLionel Sambuc pmp->pm_uid, cred));
38384d9c625SLionel Sambuc if (error)
38484d9c625SLionel Sambuc goto bad;
38584d9c625SLionel Sambuc if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95) == 0 &&
38684d9c625SLionel Sambuc vap->va_atime.tv_sec != VNOVAL)
38784d9c625SLionel Sambuc unix2dostime(&vap->va_atime, pmp->pm_gmtoff, &dep->de_ADate, NULL, NULL);
38884d9c625SLionel Sambuc if (vap->va_mtime.tv_sec != VNOVAL)
38984d9c625SLionel Sambuc unix2dostime(&vap->va_mtime, pmp->pm_gmtoff, &dep->de_MDate, &dep->de_MTime, NULL);
39084d9c625SLionel Sambuc dep->de_Attributes |= ATTR_ARCHIVE;
39184d9c625SLionel Sambuc dep->de_flag |= DE_MODIFIED;
39284d9c625SLionel Sambuc de_changed = 1;
39384d9c625SLionel Sambuc }
39484d9c625SLionel Sambuc /*
39584d9c625SLionel Sambuc * DOS files only have the ability to have their writability
39684d9c625SLionel Sambuc * attribute set, so we use the owner write bit to set the readonly
39784d9c625SLionel Sambuc * attribute.
39884d9c625SLionel Sambuc */
39984d9c625SLionel Sambuc if (vap->va_mode != (mode_t)VNOVAL) {
40084d9c625SLionel Sambuc if (vp->v_mount->mnt_flag & MNT_RDONLY) {
40184d9c625SLionel Sambuc error = EROFS;
40284d9c625SLionel Sambuc goto bad;
40384d9c625SLionel Sambuc }
40484d9c625SLionel Sambuc error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_FLAGS, vp,
40584d9c625SLionel Sambuc NULL, genfs_can_chflags(cred, vp->v_type, pmp->pm_uid, false));
40684d9c625SLionel Sambuc if (error)
40784d9c625SLionel Sambuc goto bad;
40884d9c625SLionel Sambuc /* We ignore the read and execute bits. */
40984d9c625SLionel Sambuc if (vap->va_mode & S_IWUSR)
41084d9c625SLionel Sambuc dep->de_Attributes &= ~ATTR_READONLY;
41184d9c625SLionel Sambuc else
41284d9c625SLionel Sambuc dep->de_Attributes |= ATTR_READONLY;
41384d9c625SLionel Sambuc dep->de_flag |= DE_MODIFIED;
41484d9c625SLionel Sambuc de_changed = 1;
41584d9c625SLionel Sambuc }
41684d9c625SLionel Sambuc /*
41784d9c625SLionel Sambuc * Allow the `archived' bit to be toggled.
41884d9c625SLionel Sambuc */
41984d9c625SLionel Sambuc if (vap->va_flags != VNOVAL) {
42084d9c625SLionel Sambuc if (vp->v_mount->mnt_flag & MNT_RDONLY) {
42184d9c625SLionel Sambuc error = EROFS;
42284d9c625SLionel Sambuc goto bad;
42384d9c625SLionel Sambuc }
42484d9c625SLionel Sambuc error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_FLAGS, vp,
42584d9c625SLionel Sambuc NULL, genfs_can_chflags(cred, vp->v_type, pmp->pm_uid, false));
42684d9c625SLionel Sambuc if (error)
42784d9c625SLionel Sambuc goto bad;
42884d9c625SLionel Sambuc if (vap->va_flags & SF_ARCHIVED)
42984d9c625SLionel Sambuc dep->de_Attributes &= ~ATTR_ARCHIVE;
43084d9c625SLionel Sambuc else
43184d9c625SLionel Sambuc dep->de_Attributes |= ATTR_ARCHIVE;
43284d9c625SLionel Sambuc dep->de_flag |= DE_MODIFIED;
43384d9c625SLionel Sambuc de_changed = 1;
43484d9c625SLionel Sambuc }
43584d9c625SLionel Sambuc
43684d9c625SLionel Sambuc if (de_changed) {
43784d9c625SLionel Sambuc VN_KNOTE(vp, NOTE_ATTRIB);
43884d9c625SLionel Sambuc error = deupdat(dep, 1);
43984d9c625SLionel Sambuc if (error)
44084d9c625SLionel Sambuc goto bad;
44184d9c625SLionel Sambuc }
44284d9c625SLionel Sambuc
44384d9c625SLionel Sambuc bad:
44484d9c625SLionel Sambuc fstrans_done(vp->v_mount);
44584d9c625SLionel Sambuc return error;
44684d9c625SLionel Sambuc }
44784d9c625SLionel Sambuc
44884d9c625SLionel Sambuc int
msdosfs_read(void * v)44984d9c625SLionel Sambuc msdosfs_read(void *v)
45084d9c625SLionel Sambuc {
45184d9c625SLionel Sambuc struct vop_read_args /* {
45284d9c625SLionel Sambuc struct vnode *a_vp;
45384d9c625SLionel Sambuc struct uio *a_uio;
45484d9c625SLionel Sambuc int a_ioflag;
45584d9c625SLionel Sambuc kauth_cred_t a_cred;
45684d9c625SLionel Sambuc } */ *ap = v;
45784d9c625SLionel Sambuc int error = 0;
45884d9c625SLionel Sambuc int64_t diff;
45984d9c625SLionel Sambuc int blsize;
46084d9c625SLionel Sambuc long n;
46184d9c625SLionel Sambuc long on;
46284d9c625SLionel Sambuc daddr_t lbn;
46384d9c625SLionel Sambuc vsize_t bytelen;
46484d9c625SLionel Sambuc struct buf *bp;
46584d9c625SLionel Sambuc struct vnode *vp = ap->a_vp;
46684d9c625SLionel Sambuc struct denode *dep = VTODE(vp);
46784d9c625SLionel Sambuc struct msdosfsmount *pmp = dep->de_pmp;
46884d9c625SLionel Sambuc struct uio *uio = ap->a_uio;
46984d9c625SLionel Sambuc
47084d9c625SLionel Sambuc /*
47184d9c625SLionel Sambuc * If they didn't ask for any data, then we are done.
47284d9c625SLionel Sambuc */
47384d9c625SLionel Sambuc
47484d9c625SLionel Sambuc if (uio->uio_resid == 0)
47584d9c625SLionel Sambuc return (0);
47684d9c625SLionel Sambuc if (uio->uio_offset < 0)
47784d9c625SLionel Sambuc return (EINVAL);
47884d9c625SLionel Sambuc if (uio->uio_offset >= dep->de_FileSize)
47984d9c625SLionel Sambuc return (0);
48084d9c625SLionel Sambuc
48184d9c625SLionel Sambuc fstrans_start(vp->v_mount, FSTRANS_SHARED);
48284d9c625SLionel Sambuc if (vp->v_type == VREG) {
48384d9c625SLionel Sambuc const int advice = IO_ADV_DECODE(ap->a_ioflag);
48484d9c625SLionel Sambuc
48584d9c625SLionel Sambuc while (uio->uio_resid > 0) {
48684d9c625SLionel Sambuc bytelen = MIN(dep->de_FileSize - uio->uio_offset,
48784d9c625SLionel Sambuc uio->uio_resid);
48884d9c625SLionel Sambuc
48984d9c625SLionel Sambuc if (bytelen == 0)
49084d9c625SLionel Sambuc break;
49184d9c625SLionel Sambuc error = ubc_uiomove(&vp->v_uobj, uio, bytelen, advice,
49284d9c625SLionel Sambuc UBC_READ | UBC_PARTIALOK | UBC_UNMAP_FLAG(vp));
49384d9c625SLionel Sambuc if (error)
49484d9c625SLionel Sambuc break;
49584d9c625SLionel Sambuc }
49684d9c625SLionel Sambuc dep->de_flag |= DE_ACCESS;
49784d9c625SLionel Sambuc goto out;
49884d9c625SLionel Sambuc }
49984d9c625SLionel Sambuc
50084d9c625SLionel Sambuc /* this loop is only for directories now */
50184d9c625SLionel Sambuc do {
50284d9c625SLionel Sambuc lbn = de_cluster(pmp, uio->uio_offset);
50384d9c625SLionel Sambuc on = uio->uio_offset & pmp->pm_crbomask;
50484d9c625SLionel Sambuc n = MIN(pmp->pm_bpcluster - on, uio->uio_resid);
50584d9c625SLionel Sambuc if (uio->uio_offset >= dep->de_FileSize) {
50684d9c625SLionel Sambuc fstrans_done(vp->v_mount);
50784d9c625SLionel Sambuc return (0);
50884d9c625SLionel Sambuc }
50984d9c625SLionel Sambuc /* file size (and hence diff) may be up to 4GB */
51084d9c625SLionel Sambuc diff = dep->de_FileSize - uio->uio_offset;
51184d9c625SLionel Sambuc if (diff < n)
51284d9c625SLionel Sambuc n = (long) diff;
51384d9c625SLionel Sambuc
51484d9c625SLionel Sambuc /* convert cluster # to sector # */
51584d9c625SLionel Sambuc error = pcbmap(dep, lbn, &lbn, 0, &blsize);
51684d9c625SLionel Sambuc if (error)
51784d9c625SLionel Sambuc goto bad;
51884d9c625SLionel Sambuc
51984d9c625SLionel Sambuc /*
52084d9c625SLionel Sambuc * If we are operating on a directory file then be sure to
52184d9c625SLionel Sambuc * do i/o with the vnode for the filesystem instead of the
52284d9c625SLionel Sambuc * vnode for the directory.
52384d9c625SLionel Sambuc */
52484d9c625SLionel Sambuc error = bread(pmp->pm_devvp, de_bn2kb(pmp, lbn), blsize,
525*0a6a1f1dSLionel Sambuc 0, &bp);
52684d9c625SLionel Sambuc if (error) {
52784d9c625SLionel Sambuc goto bad;
52884d9c625SLionel Sambuc }
52984d9c625SLionel Sambuc n = MIN(n, pmp->pm_bpcluster - bp->b_resid);
53084d9c625SLionel Sambuc error = uiomove((char *)bp->b_data + on, (int) n, uio);
53184d9c625SLionel Sambuc brelse(bp, 0);
53284d9c625SLionel Sambuc } while (error == 0 && uio->uio_resid > 0 && n != 0);
53384d9c625SLionel Sambuc
53484d9c625SLionel Sambuc out:
535*0a6a1f1dSLionel Sambuc if ((ap->a_ioflag & IO_SYNC) == IO_SYNC) {
536*0a6a1f1dSLionel Sambuc int uerror;
537*0a6a1f1dSLionel Sambuc
538*0a6a1f1dSLionel Sambuc uerror = deupdat(dep, 1);
539*0a6a1f1dSLionel Sambuc if (error == 0)
540*0a6a1f1dSLionel Sambuc error = uerror;
541*0a6a1f1dSLionel Sambuc }
54284d9c625SLionel Sambuc bad:
54384d9c625SLionel Sambuc fstrans_done(vp->v_mount);
54484d9c625SLionel Sambuc return (error);
54584d9c625SLionel Sambuc }
54684d9c625SLionel Sambuc
54784d9c625SLionel Sambuc /*
54884d9c625SLionel Sambuc * Write data to a file or directory.
54984d9c625SLionel Sambuc */
55084d9c625SLionel Sambuc int
msdosfs_write(void * v)55184d9c625SLionel Sambuc msdosfs_write(void *v)
55284d9c625SLionel Sambuc {
55384d9c625SLionel Sambuc struct vop_write_args /* {
55484d9c625SLionel Sambuc struct vnode *a_vp;
55584d9c625SLionel Sambuc struct uio *a_uio;
55684d9c625SLionel Sambuc int a_ioflag;
55784d9c625SLionel Sambuc kauth_cred_t a_cred;
55884d9c625SLionel Sambuc } */ *ap = v;
55984d9c625SLionel Sambuc int resid, extended = 0;
56084d9c625SLionel Sambuc int error = 0;
56184d9c625SLionel Sambuc int ioflag = ap->a_ioflag;
56284d9c625SLionel Sambuc u_long osize;
56384d9c625SLionel Sambuc u_long count;
56484d9c625SLionel Sambuc vsize_t bytelen;
56584d9c625SLionel Sambuc off_t oldoff;
56684d9c625SLionel Sambuc size_t rem;
56784d9c625SLionel Sambuc struct uio *uio = ap->a_uio;
56884d9c625SLionel Sambuc struct vnode *vp = ap->a_vp;
56984d9c625SLionel Sambuc struct denode *dep = VTODE(vp);
57084d9c625SLionel Sambuc struct msdosfsmount *pmp = dep->de_pmp;
57184d9c625SLionel Sambuc kauth_cred_t cred = ap->a_cred;
57284d9c625SLionel Sambuc bool async;
57384d9c625SLionel Sambuc
57484d9c625SLionel Sambuc #ifdef MSDOSFS_DEBUG
57584d9c625SLionel Sambuc printf("msdosfs_write(vp %p, uio %p, ioflag %x, cred %p\n",
57684d9c625SLionel Sambuc vp, uio, ioflag, cred);
57784d9c625SLionel Sambuc printf("msdosfs_write(): diroff %lu, dirclust %lu, startcluster %lu\n",
57884d9c625SLionel Sambuc dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster);
57984d9c625SLionel Sambuc #endif
58084d9c625SLionel Sambuc
58184d9c625SLionel Sambuc switch (vp->v_type) {
58284d9c625SLionel Sambuc case VREG:
58384d9c625SLionel Sambuc if (ioflag & IO_APPEND)
58484d9c625SLionel Sambuc uio->uio_offset = dep->de_FileSize;
58584d9c625SLionel Sambuc break;
58684d9c625SLionel Sambuc case VDIR:
58784d9c625SLionel Sambuc return EISDIR;
58884d9c625SLionel Sambuc default:
58984d9c625SLionel Sambuc panic("msdosfs_write(): bad file type");
59084d9c625SLionel Sambuc }
59184d9c625SLionel Sambuc
59284d9c625SLionel Sambuc if (uio->uio_offset < 0)
59384d9c625SLionel Sambuc return (EINVAL);
59484d9c625SLionel Sambuc
59584d9c625SLionel Sambuc if (uio->uio_resid == 0)
59684d9c625SLionel Sambuc return (0);
59784d9c625SLionel Sambuc
59884d9c625SLionel Sambuc /* Don't bother to try to write files larger than the fs limit */
59984d9c625SLionel Sambuc if (uio->uio_offset + uio->uio_resid > MSDOSFS_FILESIZE_MAX)
60084d9c625SLionel Sambuc return (EFBIG);
60184d9c625SLionel Sambuc
60284d9c625SLionel Sambuc fstrans_start(vp->v_mount, FSTRANS_SHARED);
60384d9c625SLionel Sambuc /*
60484d9c625SLionel Sambuc * If the offset we are starting the write at is beyond the end of
60584d9c625SLionel Sambuc * the file, then they've done a seek. Unix filesystems allow
60684d9c625SLionel Sambuc * files with holes in them, DOS doesn't so we must fill the hole
60784d9c625SLionel Sambuc * with zeroed blocks.
60884d9c625SLionel Sambuc */
60984d9c625SLionel Sambuc if (uio->uio_offset > dep->de_FileSize) {
61084d9c625SLionel Sambuc if ((error = deextend(dep, uio->uio_offset, cred)) != 0) {
61184d9c625SLionel Sambuc fstrans_done(vp->v_mount);
61284d9c625SLionel Sambuc return (error);
61384d9c625SLionel Sambuc }
61484d9c625SLionel Sambuc }
61584d9c625SLionel Sambuc
61684d9c625SLionel Sambuc /*
61784d9c625SLionel Sambuc * Remember some values in case the write fails.
61884d9c625SLionel Sambuc */
61984d9c625SLionel Sambuc async = vp->v_mount->mnt_flag & MNT_ASYNC;
62084d9c625SLionel Sambuc resid = uio->uio_resid;
62184d9c625SLionel Sambuc osize = dep->de_FileSize;
62284d9c625SLionel Sambuc
62384d9c625SLionel Sambuc /*
62484d9c625SLionel Sambuc * If we write beyond the end of the file, extend it to its ultimate
62584d9c625SLionel Sambuc * size ahead of the time to hopefully get a contiguous area.
62684d9c625SLionel Sambuc */
62784d9c625SLionel Sambuc if (uio->uio_offset + resid > osize) {
62884d9c625SLionel Sambuc count = de_clcount(pmp, uio->uio_offset + resid) -
62984d9c625SLionel Sambuc de_clcount(pmp, osize);
63084d9c625SLionel Sambuc if ((error = extendfile(dep, count, NULL, NULL, 0)))
63184d9c625SLionel Sambuc goto errexit;
63284d9c625SLionel Sambuc
63384d9c625SLionel Sambuc dep->de_FileSize = uio->uio_offset + resid;
63484d9c625SLionel Sambuc /* hint uvm to not read in extended part */
63584d9c625SLionel Sambuc uvm_vnp_setwritesize(vp, dep->de_FileSize);
63684d9c625SLionel Sambuc /* zero out the remainder of the last page */
63784d9c625SLionel Sambuc rem = round_page(dep->de_FileSize) - dep->de_FileSize;
63884d9c625SLionel Sambuc if (rem > 0)
63984d9c625SLionel Sambuc ubc_zerorange(&vp->v_uobj, (off_t)dep->de_FileSize,
64084d9c625SLionel Sambuc rem, UBC_UNMAP_FLAG(vp));
64184d9c625SLionel Sambuc extended = 1;
64284d9c625SLionel Sambuc }
64384d9c625SLionel Sambuc
64484d9c625SLionel Sambuc do {
64584d9c625SLionel Sambuc oldoff = uio->uio_offset;
64684d9c625SLionel Sambuc bytelen = uio->uio_resid;
64784d9c625SLionel Sambuc
64884d9c625SLionel Sambuc error = ubc_uiomove(&vp->v_uobj, uio, bytelen,
64984d9c625SLionel Sambuc IO_ADV_DECODE(ioflag), UBC_WRITE | UBC_UNMAP_FLAG(vp));
65084d9c625SLionel Sambuc if (error)
65184d9c625SLionel Sambuc break;
65284d9c625SLionel Sambuc
65384d9c625SLionel Sambuc /*
65484d9c625SLionel Sambuc * flush what we just wrote if necessary.
65584d9c625SLionel Sambuc * XXXUBC simplistic async flushing.
65684d9c625SLionel Sambuc */
65784d9c625SLionel Sambuc
65884d9c625SLionel Sambuc if (!async && oldoff >> 16 != uio->uio_offset >> 16) {
65984d9c625SLionel Sambuc mutex_enter(vp->v_interlock);
66084d9c625SLionel Sambuc error = VOP_PUTPAGES(vp, (oldoff >> 16) << 16,
66184d9c625SLionel Sambuc (uio->uio_offset >> 16) << 16,
66284d9c625SLionel Sambuc PGO_CLEANIT | PGO_LAZY);
66384d9c625SLionel Sambuc }
66484d9c625SLionel Sambuc } while (error == 0 && uio->uio_resid > 0);
66584d9c625SLionel Sambuc
66684d9c625SLionel Sambuc /* set final size */
66784d9c625SLionel Sambuc uvm_vnp_setsize(vp, dep->de_FileSize);
66884d9c625SLionel Sambuc if (error == 0 && ioflag & IO_SYNC) {
66984d9c625SLionel Sambuc mutex_enter(vp->v_interlock);
67084d9c625SLionel Sambuc error = VOP_PUTPAGES(vp, trunc_page(oldoff),
67184d9c625SLionel Sambuc round_page(oldoff + bytelen), PGO_CLEANIT | PGO_SYNCIO);
67284d9c625SLionel Sambuc }
67384d9c625SLionel Sambuc dep->de_flag |= DE_UPDATE;
67484d9c625SLionel Sambuc
67584d9c625SLionel Sambuc /*
67684d9c625SLionel Sambuc * If the write failed and they want us to, truncate the file back
67784d9c625SLionel Sambuc * to the size it was before the write was attempted.
67884d9c625SLionel Sambuc */
67984d9c625SLionel Sambuc errexit:
68084d9c625SLionel Sambuc if (resid > uio->uio_resid)
68184d9c625SLionel Sambuc VN_KNOTE(vp, NOTE_WRITE | (extended ? NOTE_EXTEND : 0));
68284d9c625SLionel Sambuc if (error) {
68384d9c625SLionel Sambuc detrunc(dep, osize, ioflag & IO_SYNC, NOCRED);
68484d9c625SLionel Sambuc uio->uio_offset -= resid - uio->uio_resid;
68584d9c625SLionel Sambuc uio->uio_resid = resid;
68684d9c625SLionel Sambuc } else if ((ioflag & IO_SYNC) == IO_SYNC)
68784d9c625SLionel Sambuc error = deupdat(dep, 1);
68884d9c625SLionel Sambuc fstrans_done(vp->v_mount);
68984d9c625SLionel Sambuc KASSERT(vp->v_size == dep->de_FileSize);
69084d9c625SLionel Sambuc return (error);
69184d9c625SLionel Sambuc }
69284d9c625SLionel Sambuc
69384d9c625SLionel Sambuc int
msdosfs_update(struct vnode * vp,const struct timespec * acc,const struct timespec * mod,int flags)69484d9c625SLionel Sambuc msdosfs_update(struct vnode *vp, const struct timespec *acc,
69584d9c625SLionel Sambuc const struct timespec *mod, int flags)
69684d9c625SLionel Sambuc {
69784d9c625SLionel Sambuc struct buf *bp;
69884d9c625SLionel Sambuc struct direntry *dirp;
69984d9c625SLionel Sambuc struct denode *dep;
70084d9c625SLionel Sambuc int error;
70184d9c625SLionel Sambuc
70284d9c625SLionel Sambuc if (vp->v_mount->mnt_flag & MNT_RDONLY)
70384d9c625SLionel Sambuc return (0);
70484d9c625SLionel Sambuc dep = VTODE(vp);
70584d9c625SLionel Sambuc DETIMES(dep, acc, mod, NULL, dep->de_pmp->pm_gmtoff);
70684d9c625SLionel Sambuc if ((dep->de_flag & DE_MODIFIED) == 0)
70784d9c625SLionel Sambuc return (0);
70884d9c625SLionel Sambuc dep->de_flag &= ~DE_MODIFIED;
70984d9c625SLionel Sambuc if (dep->de_Attributes & ATTR_DIRECTORY)
71084d9c625SLionel Sambuc return (0);
71184d9c625SLionel Sambuc if (dep->de_refcnt <= 0)
71284d9c625SLionel Sambuc return (0);
71384d9c625SLionel Sambuc error = readde(dep, &bp, &dirp);
71484d9c625SLionel Sambuc if (error)
71584d9c625SLionel Sambuc return (error);
71684d9c625SLionel Sambuc DE_EXTERNALIZE(dirp, dep);
71784d9c625SLionel Sambuc if (flags & (UPDATE_WAIT|UPDATE_DIROP))
71884d9c625SLionel Sambuc return (bwrite(bp));
71984d9c625SLionel Sambuc else {
72084d9c625SLionel Sambuc bdwrite(bp);
72184d9c625SLionel Sambuc return (0);
72284d9c625SLionel Sambuc }
72384d9c625SLionel Sambuc }
72484d9c625SLionel Sambuc
72584d9c625SLionel Sambuc /*
72684d9c625SLionel Sambuc * Flush the blocks of a file to disk.
72784d9c625SLionel Sambuc *
72884d9c625SLionel Sambuc * This function is worthless for vnodes that represent directories. Maybe we
72984d9c625SLionel Sambuc * could just do a sync if they try an fsync on a directory file.
73084d9c625SLionel Sambuc */
73184d9c625SLionel Sambuc int
msdosfs_remove(void * v)73284d9c625SLionel Sambuc msdosfs_remove(void *v)
73384d9c625SLionel Sambuc {
73484d9c625SLionel Sambuc struct vop_remove_args /* {
73584d9c625SLionel Sambuc struct vnode *a_dvp;
73684d9c625SLionel Sambuc struct vnode *a_vp;
73784d9c625SLionel Sambuc struct componentname *a_cnp;
73884d9c625SLionel Sambuc } */ *ap = v;
73984d9c625SLionel Sambuc struct denode *dep = VTODE(ap->a_vp);
74084d9c625SLionel Sambuc struct denode *ddep = VTODE(ap->a_dvp);
74184d9c625SLionel Sambuc int error;
74284d9c625SLionel Sambuc
74384d9c625SLionel Sambuc fstrans_start(ap->a_dvp->v_mount, FSTRANS_SHARED);
74484d9c625SLionel Sambuc if (ap->a_vp->v_type == VDIR)
74584d9c625SLionel Sambuc error = EPERM;
74684d9c625SLionel Sambuc else
74784d9c625SLionel Sambuc error = removede(ddep, dep);
74884d9c625SLionel Sambuc #ifdef MSDOSFS_DEBUG
74984d9c625SLionel Sambuc printf("msdosfs_remove(), dep %p, v_usecount %d\n",
75084d9c625SLionel Sambuc dep, ap->a_vp->v_usecount);
75184d9c625SLionel Sambuc #endif
75284d9c625SLionel Sambuc VN_KNOTE(ap->a_vp, NOTE_DELETE);
75384d9c625SLionel Sambuc VN_KNOTE(ap->a_dvp, NOTE_WRITE);
75484d9c625SLionel Sambuc if (ddep == dep)
75584d9c625SLionel Sambuc vrele(ap->a_vp);
75684d9c625SLionel Sambuc else
75784d9c625SLionel Sambuc vput(ap->a_vp); /* causes msdosfs_inactive() to be called
75884d9c625SLionel Sambuc * via vrele() */
75984d9c625SLionel Sambuc vput(ap->a_dvp);
76084d9c625SLionel Sambuc fstrans_done(ap->a_dvp->v_mount);
76184d9c625SLionel Sambuc return (error);
76284d9c625SLionel Sambuc }
76384d9c625SLionel Sambuc
76484d9c625SLionel Sambuc /*
76584d9c625SLionel Sambuc * Renames on files require moving the denode to a new hash queue since the
76684d9c625SLionel Sambuc * denode's location is used to compute which hash queue to put the file
76784d9c625SLionel Sambuc * in. Unless it is a rename in place. For example "mv a b".
76884d9c625SLionel Sambuc *
76984d9c625SLionel Sambuc * What follows is the basic algorithm:
77084d9c625SLionel Sambuc *
77184d9c625SLionel Sambuc * if (file move) {
77284d9c625SLionel Sambuc * if (dest file exists) {
77384d9c625SLionel Sambuc * remove dest file
77484d9c625SLionel Sambuc * }
77584d9c625SLionel Sambuc * if (dest and src in same directory) {
77684d9c625SLionel Sambuc * rewrite name in existing directory slot
77784d9c625SLionel Sambuc * } else {
77884d9c625SLionel Sambuc * write new entry in dest directory
77984d9c625SLionel Sambuc * update offset and dirclust in denode
78084d9c625SLionel Sambuc * move denode to new hash chain
78184d9c625SLionel Sambuc * clear old directory entry
78284d9c625SLionel Sambuc * }
78384d9c625SLionel Sambuc * } else {
78484d9c625SLionel Sambuc * directory move
78584d9c625SLionel Sambuc * if (dest directory exists) {
78684d9c625SLionel Sambuc * if (dest is not empty) {
78784d9c625SLionel Sambuc * return ENOTEMPTY
78884d9c625SLionel Sambuc * }
78984d9c625SLionel Sambuc * remove dest directory
79084d9c625SLionel Sambuc * }
79184d9c625SLionel Sambuc * if (dest and src in same directory) {
79284d9c625SLionel Sambuc * rewrite name in existing entry
79384d9c625SLionel Sambuc * } else {
79484d9c625SLionel Sambuc * be sure dest is not a child of src directory
79584d9c625SLionel Sambuc * write entry in dest directory
79684d9c625SLionel Sambuc * update "." and ".." in moved directory
79784d9c625SLionel Sambuc * update offset and dirclust in denode
79884d9c625SLionel Sambuc * move denode to new hash chain
79984d9c625SLionel Sambuc * clear old directory entry for moved directory
80084d9c625SLionel Sambuc * }
80184d9c625SLionel Sambuc * }
80284d9c625SLionel Sambuc *
80384d9c625SLionel Sambuc * On entry:
80484d9c625SLionel Sambuc * source's parent directory is unlocked
80584d9c625SLionel Sambuc * source file or directory is unlocked
80684d9c625SLionel Sambuc * destination's parent directory is locked
80784d9c625SLionel Sambuc * destination file or directory is locked if it exists
80884d9c625SLionel Sambuc *
80984d9c625SLionel Sambuc * On exit:
81084d9c625SLionel Sambuc * all denodes should be released
81184d9c625SLionel Sambuc *
81284d9c625SLionel Sambuc * Notes:
81384d9c625SLionel Sambuc * I'm not sure how the memory containing the pathnames pointed at by the
81484d9c625SLionel Sambuc * componentname structures is freed, there may be some memory bleeding
81584d9c625SLionel Sambuc * for each rename done.
81684d9c625SLionel Sambuc *
81784d9c625SLionel Sambuc * --More-- Notes:
81884d9c625SLionel Sambuc * This routine needs help. badly.
81984d9c625SLionel Sambuc */
82084d9c625SLionel Sambuc int
msdosfs_rename(void * v)82184d9c625SLionel Sambuc msdosfs_rename(void *v)
82284d9c625SLionel Sambuc {
82384d9c625SLionel Sambuc struct vop_rename_args /* {
82484d9c625SLionel Sambuc struct vnode *a_fdvp;
82584d9c625SLionel Sambuc struct vnode *a_fvp;
82684d9c625SLionel Sambuc struct componentname *a_fcnp;
82784d9c625SLionel Sambuc struct vnode *a_tdvp;
82884d9c625SLionel Sambuc struct vnode *a_tvp;
82984d9c625SLionel Sambuc struct componentname *a_tcnp;
83084d9c625SLionel Sambuc } */ *ap = v;
83184d9c625SLionel Sambuc struct vnode *tvp = ap->a_tvp;
83284d9c625SLionel Sambuc struct vnode *tdvp = ap->a_tdvp;
83384d9c625SLionel Sambuc struct vnode *fvp = ap->a_fvp;
83484d9c625SLionel Sambuc struct vnode *fdvp = ap->a_fdvp;
83584d9c625SLionel Sambuc struct mount *mp = fdvp->v_mount;
83684d9c625SLionel Sambuc struct componentname *tcnp = ap->a_tcnp;
83784d9c625SLionel Sambuc struct componentname *fcnp = ap->a_fcnp;
83884d9c625SLionel Sambuc struct denode *ip, *xp, *dp, *zp;
83984d9c625SLionel Sambuc u_char toname[12], oldname[12];
84084d9c625SLionel Sambuc u_long from_diroffset, to_diroffset;
84184d9c625SLionel Sambuc u_char to_count;
84284d9c625SLionel Sambuc int doingdirectory = 0, newparent = 0;
84384d9c625SLionel Sambuc int error;
84484d9c625SLionel Sambuc u_long cn;
84584d9c625SLionel Sambuc daddr_t bn;
84684d9c625SLionel Sambuc struct msdosfsmount *pmp;
84784d9c625SLionel Sambuc struct direntry *dotdotp;
84884d9c625SLionel Sambuc struct buf *bp;
84984d9c625SLionel Sambuc
85084d9c625SLionel Sambuc pmp = VFSTOMSDOSFS(fdvp->v_mount);
85184d9c625SLionel Sambuc
85284d9c625SLionel Sambuc /*
85384d9c625SLionel Sambuc * Check for cross-device rename.
85484d9c625SLionel Sambuc */
85584d9c625SLionel Sambuc if ((fvp->v_mount != tdvp->v_mount) ||
85684d9c625SLionel Sambuc (tvp && (fvp->v_mount != tvp->v_mount))) {
85784d9c625SLionel Sambuc error = EXDEV;
85884d9c625SLionel Sambuc abortit:
85984d9c625SLionel Sambuc VOP_ABORTOP(tdvp, tcnp);
86084d9c625SLionel Sambuc if (tdvp == tvp)
86184d9c625SLionel Sambuc vrele(tdvp);
86284d9c625SLionel Sambuc else
86384d9c625SLionel Sambuc vput(tdvp);
86484d9c625SLionel Sambuc if (tvp)
86584d9c625SLionel Sambuc vput(tvp);
86684d9c625SLionel Sambuc VOP_ABORTOP(fdvp, fcnp);
86784d9c625SLionel Sambuc vrele(fdvp);
86884d9c625SLionel Sambuc vrele(fvp);
86984d9c625SLionel Sambuc return (error);
87084d9c625SLionel Sambuc }
87184d9c625SLionel Sambuc
87284d9c625SLionel Sambuc /*
87384d9c625SLionel Sambuc * If source and dest are the same, do nothing.
87484d9c625SLionel Sambuc */
87584d9c625SLionel Sambuc if (tvp == fvp) {
87684d9c625SLionel Sambuc error = 0;
87784d9c625SLionel Sambuc goto abortit;
87884d9c625SLionel Sambuc }
87984d9c625SLionel Sambuc
88084d9c625SLionel Sambuc /*
88184d9c625SLionel Sambuc * XXX: This can deadlock since we hold tdvp/tvp locked.
88284d9c625SLionel Sambuc * But I'm not going to fix it now.
88384d9c625SLionel Sambuc */
88484d9c625SLionel Sambuc if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0)
88584d9c625SLionel Sambuc goto abortit;
88684d9c625SLionel Sambuc dp = VTODE(fdvp);
88784d9c625SLionel Sambuc ip = VTODE(fvp);
88884d9c625SLionel Sambuc
88984d9c625SLionel Sambuc /*
89084d9c625SLionel Sambuc * Be sure we are not renaming ".", "..", or an alias of ".". This
89184d9c625SLionel Sambuc * leads to a crippled directory tree. It's pretty tough to do a
89284d9c625SLionel Sambuc * "ls" or "pwd" with the "." directory entry missing, and "cd .."
89384d9c625SLionel Sambuc * doesn't work if the ".." entry is missing.
89484d9c625SLionel Sambuc */
89584d9c625SLionel Sambuc if (ip->de_Attributes & ATTR_DIRECTORY) {
89684d9c625SLionel Sambuc /*
89784d9c625SLionel Sambuc * Avoid ".", "..", and aliases of "." for obvious reasons.
89884d9c625SLionel Sambuc */
89984d9c625SLionel Sambuc if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
90084d9c625SLionel Sambuc dp == ip ||
90184d9c625SLionel Sambuc (fcnp->cn_flags & ISDOTDOT) ||
90284d9c625SLionel Sambuc (tcnp->cn_flags & ISDOTDOT) ||
90384d9c625SLionel Sambuc (ip->de_flag & DE_RENAME)) {
90484d9c625SLionel Sambuc VOP_UNLOCK(fvp);
90584d9c625SLionel Sambuc error = EINVAL;
90684d9c625SLionel Sambuc goto abortit;
90784d9c625SLionel Sambuc }
90884d9c625SLionel Sambuc ip->de_flag |= DE_RENAME;
90984d9c625SLionel Sambuc doingdirectory++;
91084d9c625SLionel Sambuc }
91184d9c625SLionel Sambuc VN_KNOTE(fdvp, NOTE_WRITE); /* XXXLUKEM/XXX: right place? */
91284d9c625SLionel Sambuc
91384d9c625SLionel Sambuc fstrans_start(mp, FSTRANS_SHARED);
91484d9c625SLionel Sambuc /*
91584d9c625SLionel Sambuc * When the target exists, both the directory
91684d9c625SLionel Sambuc * and target vnodes are returned locked.
91784d9c625SLionel Sambuc */
91884d9c625SLionel Sambuc dp = VTODE(tdvp);
91984d9c625SLionel Sambuc xp = tvp ? VTODE(tvp) : NULL;
92084d9c625SLionel Sambuc /*
92184d9c625SLionel Sambuc * Remember direntry place to use for destination
92284d9c625SLionel Sambuc */
92384d9c625SLionel Sambuc to_diroffset = dp->de_fndoffset;
92484d9c625SLionel Sambuc to_count = dp->de_fndcnt;
92584d9c625SLionel Sambuc
92684d9c625SLionel Sambuc /*
92784d9c625SLionel Sambuc * If ".." must be changed (ie the directory gets a new
92884d9c625SLionel Sambuc * parent) then the source directory must not be in the
92984d9c625SLionel Sambuc * directory hierarchy above the target, as this would
93084d9c625SLionel Sambuc * orphan everything below the source directory. Also
93184d9c625SLionel Sambuc * the user must have write permission in the source so
93284d9c625SLionel Sambuc * as to be able to change "..". We must repeat the call
93384d9c625SLionel Sambuc * to namei, as the parent directory is unlocked by the
93484d9c625SLionel Sambuc * call to doscheckpath().
93584d9c625SLionel Sambuc */
93684d9c625SLionel Sambuc error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred);
93784d9c625SLionel Sambuc VOP_UNLOCK(fvp);
93884d9c625SLionel Sambuc if (VTODE(fdvp)->de_StartCluster != VTODE(tdvp)->de_StartCluster)
93984d9c625SLionel Sambuc newparent = 1;
94084d9c625SLionel Sambuc
94184d9c625SLionel Sambuc if (doingdirectory && newparent) {
94284d9c625SLionel Sambuc if (error) /* write access check above */
94384d9c625SLionel Sambuc goto tdvpbad;
94484d9c625SLionel Sambuc if (xp != NULL)
94584d9c625SLionel Sambuc vput(tvp);
94684d9c625SLionel Sambuc tvp = NULL;
94784d9c625SLionel Sambuc /*
94884d9c625SLionel Sambuc * doscheckpath() vput()'s tdvp (dp == VTODE(tdvp)),
94984d9c625SLionel Sambuc * so we have to get an extra ref to it first, and
95084d9c625SLionel Sambuc * because it's been unlocked we need to do a relookup
95184d9c625SLionel Sambuc * afterwards in case tvp has changed.
95284d9c625SLionel Sambuc */
95384d9c625SLionel Sambuc vref(tdvp);
95484d9c625SLionel Sambuc if ((error = doscheckpath(ip, dp)) != 0)
95584d9c625SLionel Sambuc goto bad;
95684d9c625SLionel Sambuc vn_lock(tdvp, LK_EXCLUSIVE | LK_RETRY);
95784d9c625SLionel Sambuc if ((error = relookup(tdvp, &tvp, tcnp, 0)) != 0) {
95884d9c625SLionel Sambuc VOP_UNLOCK(tdvp);
95984d9c625SLionel Sambuc goto bad;
96084d9c625SLionel Sambuc }
96184d9c625SLionel Sambuc dp = VTODE(tdvp);
96284d9c625SLionel Sambuc xp = tvp ? VTODE(tvp) : NULL;
96384d9c625SLionel Sambuc }
96484d9c625SLionel Sambuc
96584d9c625SLionel Sambuc if (xp != NULL) {
96684d9c625SLionel Sambuc /*
96784d9c625SLionel Sambuc * Target must be empty if a directory and have no links
96884d9c625SLionel Sambuc * to it. Also, ensure source and target are compatible
96984d9c625SLionel Sambuc * (both directories, or both not directories).
97084d9c625SLionel Sambuc */
97184d9c625SLionel Sambuc if (xp->de_Attributes & ATTR_DIRECTORY) {
97284d9c625SLionel Sambuc if (!dosdirempty(xp)) {
97384d9c625SLionel Sambuc error = ENOTEMPTY;
97484d9c625SLionel Sambuc goto tdvpbad;
97584d9c625SLionel Sambuc }
97684d9c625SLionel Sambuc if (!doingdirectory) {
97784d9c625SLionel Sambuc error = ENOTDIR;
97884d9c625SLionel Sambuc goto tdvpbad;
97984d9c625SLionel Sambuc }
98084d9c625SLionel Sambuc } else if (doingdirectory) {
98184d9c625SLionel Sambuc error = EISDIR;
98284d9c625SLionel Sambuc goto tdvpbad;
98384d9c625SLionel Sambuc }
98484d9c625SLionel Sambuc if ((error = removede(dp, xp)) != 0)
98584d9c625SLionel Sambuc goto tdvpbad;
98684d9c625SLionel Sambuc VN_KNOTE(tdvp, NOTE_WRITE);
98784d9c625SLionel Sambuc VN_KNOTE(tvp, NOTE_DELETE);
98884d9c625SLionel Sambuc cache_purge(tvp);
98984d9c625SLionel Sambuc vput(tvp);
99084d9c625SLionel Sambuc tvp = NULL;
99184d9c625SLionel Sambuc xp = NULL;
99284d9c625SLionel Sambuc }
99384d9c625SLionel Sambuc
99484d9c625SLionel Sambuc /*
99584d9c625SLionel Sambuc * Convert the filename in tcnp into a dos filename. We copy this
99684d9c625SLionel Sambuc * into the denode and directory entry for the destination
99784d9c625SLionel Sambuc * file/directory.
99884d9c625SLionel Sambuc */
99984d9c625SLionel Sambuc if ((error = uniqdosname(VTODE(tdvp), tcnp, toname)) != 0) {
100084d9c625SLionel Sambuc fstrans_done(mp);
100184d9c625SLionel Sambuc goto abortit;
100284d9c625SLionel Sambuc }
100384d9c625SLionel Sambuc
100484d9c625SLionel Sambuc /*
100584d9c625SLionel Sambuc * Since from wasn't locked at various places above,
100684d9c625SLionel Sambuc * have to do a relookup here.
100784d9c625SLionel Sambuc */
100884d9c625SLionel Sambuc fcnp->cn_flags &= ~MODMASK;
100984d9c625SLionel Sambuc fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
101084d9c625SLionel Sambuc VOP_UNLOCK(tdvp);
101184d9c625SLionel Sambuc vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY);
101284d9c625SLionel Sambuc if ((error = relookup(fdvp, &fvp, fcnp, 0))) {
101384d9c625SLionel Sambuc VOP_UNLOCK(fdvp);
101484d9c625SLionel Sambuc vrele(ap->a_fvp);
101584d9c625SLionel Sambuc vrele(tdvp);
101684d9c625SLionel Sambuc fstrans_done(mp);
101784d9c625SLionel Sambuc return (error);
101884d9c625SLionel Sambuc }
101984d9c625SLionel Sambuc if (fvp == NULL) {
102084d9c625SLionel Sambuc /*
102184d9c625SLionel Sambuc * From name has disappeared.
102284d9c625SLionel Sambuc */
102384d9c625SLionel Sambuc if (doingdirectory)
102484d9c625SLionel Sambuc panic("rename: lost dir entry");
102584d9c625SLionel Sambuc vput(fdvp);
102684d9c625SLionel Sambuc vrele(ap->a_fvp);
102784d9c625SLionel Sambuc vrele(tdvp);
102884d9c625SLionel Sambuc fstrans_done(mp);
102984d9c625SLionel Sambuc return 0;
103084d9c625SLionel Sambuc }
103184d9c625SLionel Sambuc VOP_UNLOCK(fdvp);
103284d9c625SLionel Sambuc xp = VTODE(fvp);
103384d9c625SLionel Sambuc zp = VTODE(fdvp);
103484d9c625SLionel Sambuc from_diroffset = zp->de_fndoffset;
103584d9c625SLionel Sambuc
103684d9c625SLionel Sambuc /*
103784d9c625SLionel Sambuc * Ensure that the directory entry still exists and has not
103884d9c625SLionel Sambuc * changed till now. If the source is a file the entry may
103984d9c625SLionel Sambuc * have been unlinked or renamed. In either case there is
104084d9c625SLionel Sambuc * no further work to be done. If the source is a directory
104184d9c625SLionel Sambuc * then it cannot have been rmdir'ed or renamed; this is
104284d9c625SLionel Sambuc * prohibited by the DE_RENAME flag.
104384d9c625SLionel Sambuc */
104484d9c625SLionel Sambuc if (xp != ip) {
104584d9c625SLionel Sambuc if (doingdirectory)
104684d9c625SLionel Sambuc panic("rename: lost dir entry");
104784d9c625SLionel Sambuc vrele(ap->a_fvp);
104884d9c625SLionel Sambuc xp = NULL;
104984d9c625SLionel Sambuc } else {
105084d9c625SLionel Sambuc vrele(fvp);
105184d9c625SLionel Sambuc xp = NULL;
105284d9c625SLionel Sambuc
105384d9c625SLionel Sambuc /*
105484d9c625SLionel Sambuc * First write a new entry in the destination
105584d9c625SLionel Sambuc * directory and mark the entry in the source directory
105684d9c625SLionel Sambuc * as deleted. Then move the denode to the correct hash
105784d9c625SLionel Sambuc * chain for its new location in the filesystem. And, if
105884d9c625SLionel Sambuc * we moved a directory, then update its .. entry to point
105984d9c625SLionel Sambuc * to the new parent directory.
106084d9c625SLionel Sambuc */
106184d9c625SLionel Sambuc memcpy(oldname, ip->de_Name, 11);
106284d9c625SLionel Sambuc memcpy(ip->de_Name, toname, 11); /* update denode */
106384d9c625SLionel Sambuc dp->de_fndoffset = to_diroffset;
106484d9c625SLionel Sambuc dp->de_fndcnt = to_count;
106584d9c625SLionel Sambuc error = createde(ip, dp, (struct denode **)0, tcnp);
106684d9c625SLionel Sambuc if (error) {
106784d9c625SLionel Sambuc memcpy(ip->de_Name, oldname, 11);
106884d9c625SLionel Sambuc VOP_UNLOCK(fvp);
106984d9c625SLionel Sambuc goto bad;
107084d9c625SLionel Sambuc }
107184d9c625SLionel Sambuc ip->de_refcnt++;
107284d9c625SLionel Sambuc zp->de_fndoffset = from_diroffset;
107384d9c625SLionel Sambuc if ((error = removede(zp, ip)) != 0) {
107484d9c625SLionel Sambuc /* XXX should really panic here, fs is corrupt */
107584d9c625SLionel Sambuc VOP_UNLOCK(fvp);
107684d9c625SLionel Sambuc goto bad;
107784d9c625SLionel Sambuc }
107884d9c625SLionel Sambuc cache_purge(fvp);
107984d9c625SLionel Sambuc if (!doingdirectory) {
1080*0a6a1f1dSLionel Sambuc struct denode_key old_key = ip->de_key;
1081*0a6a1f1dSLionel Sambuc struct denode_key new_key = ip->de_key;
1082*0a6a1f1dSLionel Sambuc
108384d9c625SLionel Sambuc error = pcbmap(dp, de_cluster(pmp, to_diroffset), 0,
1084*0a6a1f1dSLionel Sambuc &new_key.dk_dirclust, 0);
108584d9c625SLionel Sambuc if (error) {
108684d9c625SLionel Sambuc /* XXX should really panic here, fs is corrupt */
108784d9c625SLionel Sambuc VOP_UNLOCK(fvp);
108884d9c625SLionel Sambuc goto bad;
108984d9c625SLionel Sambuc }
1090*0a6a1f1dSLionel Sambuc new_key.dk_diroffset = to_diroffset;
1091*0a6a1f1dSLionel Sambuc if (new_key.dk_dirclust != MSDOSFSROOT)
1092*0a6a1f1dSLionel Sambuc new_key.dk_diroffset &= pmp->pm_crbomask;
1093*0a6a1f1dSLionel Sambuc vcache_rekey_enter(pmp->pm_mountp, fvp, &old_key,
1094*0a6a1f1dSLionel Sambuc sizeof(old_key), &new_key, sizeof(new_key));
1095*0a6a1f1dSLionel Sambuc ip->de_key = new_key;
1096*0a6a1f1dSLionel Sambuc vcache_rekey_exit(pmp->pm_mountp, fvp, &old_key,
1097*0a6a1f1dSLionel Sambuc sizeof(old_key), &ip->de_key, sizeof(ip->de_key));
109884d9c625SLionel Sambuc }
109984d9c625SLionel Sambuc }
110084d9c625SLionel Sambuc
110184d9c625SLionel Sambuc /*
110284d9c625SLionel Sambuc * If we moved a directory to a new parent directory, then we must
110384d9c625SLionel Sambuc * fixup the ".." entry in the moved directory.
110484d9c625SLionel Sambuc */
110584d9c625SLionel Sambuc if (doingdirectory && newparent) {
110684d9c625SLionel Sambuc cn = ip->de_StartCluster;
110784d9c625SLionel Sambuc if (cn == MSDOSFSROOT) {
110884d9c625SLionel Sambuc /* this should never happen */
110984d9c625SLionel Sambuc panic("msdosfs_rename: updating .. in root directory?");
111084d9c625SLionel Sambuc } else
111184d9c625SLionel Sambuc bn = cntobn(pmp, cn);
111284d9c625SLionel Sambuc error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn),
1113*0a6a1f1dSLionel Sambuc pmp->pm_bpcluster, B_MODIFY, &bp);
111484d9c625SLionel Sambuc if (error) {
111584d9c625SLionel Sambuc /* XXX should really panic here, fs is corrupt */
111684d9c625SLionel Sambuc VOP_UNLOCK(fvp);
111784d9c625SLionel Sambuc goto bad;
111884d9c625SLionel Sambuc }
111984d9c625SLionel Sambuc dotdotp = (struct direntry *)bp->b_data + 1;
112084d9c625SLionel Sambuc putushort(dotdotp->deStartCluster, dp->de_StartCluster);
112184d9c625SLionel Sambuc if (FAT32(pmp)) {
112284d9c625SLionel Sambuc putushort(dotdotp->deHighClust,
112384d9c625SLionel Sambuc dp->de_StartCluster >> 16);
112484d9c625SLionel Sambuc } else {
112584d9c625SLionel Sambuc putushort(dotdotp->deHighClust, 0);
112684d9c625SLionel Sambuc }
112784d9c625SLionel Sambuc if ((error = bwrite(bp)) != 0) {
112884d9c625SLionel Sambuc /* XXX should really panic here, fs is corrupt */
112984d9c625SLionel Sambuc VOP_UNLOCK(fvp);
113084d9c625SLionel Sambuc goto bad;
113184d9c625SLionel Sambuc }
113284d9c625SLionel Sambuc }
113384d9c625SLionel Sambuc
113484d9c625SLionel Sambuc VN_KNOTE(fvp, NOTE_RENAME);
113584d9c625SLionel Sambuc VOP_UNLOCK(fvp);
113684d9c625SLionel Sambuc bad:
113784d9c625SLionel Sambuc if (tvp)
113884d9c625SLionel Sambuc vput(tvp);
113984d9c625SLionel Sambuc vrele(tdvp);
114084d9c625SLionel Sambuc ip->de_flag &= ~DE_RENAME;
114184d9c625SLionel Sambuc vrele(fdvp);
114284d9c625SLionel Sambuc vrele(fvp);
114384d9c625SLionel Sambuc fstrans_done(mp);
114484d9c625SLionel Sambuc return (error);
114584d9c625SLionel Sambuc
114684d9c625SLionel Sambuc /* XXX: uuuh */
114784d9c625SLionel Sambuc tdvpbad:
114884d9c625SLionel Sambuc VOP_UNLOCK(tdvp);
114984d9c625SLionel Sambuc goto bad;
115084d9c625SLionel Sambuc }
115184d9c625SLionel Sambuc
115284d9c625SLionel Sambuc static const struct {
115384d9c625SLionel Sambuc struct direntry dot;
115484d9c625SLionel Sambuc struct direntry dotdot;
115584d9c625SLionel Sambuc } dosdirtemplate = {
115684d9c625SLionel Sambuc { ". ", " ", /* the . entry */
115784d9c625SLionel Sambuc ATTR_DIRECTORY, /* file attribute */
115884d9c625SLionel Sambuc 0, /* reserved */
115984d9c625SLionel Sambuc 0, { 0, 0 }, { 0, 0 }, /* create time & date */
116084d9c625SLionel Sambuc { 0, 0 }, /* access date */
116184d9c625SLionel Sambuc { 0, 0 }, /* high bits of start cluster */
116284d9c625SLionel Sambuc { 210, 4 }, { 210, 4 }, /* modify time & date */
116384d9c625SLionel Sambuc { 0, 0 }, /* startcluster */
116484d9c625SLionel Sambuc { 0, 0, 0, 0 } /* filesize */
116584d9c625SLionel Sambuc },
116684d9c625SLionel Sambuc { ".. ", " ", /* the .. entry */
116784d9c625SLionel Sambuc ATTR_DIRECTORY, /* file attribute */
116884d9c625SLionel Sambuc 0, /* reserved */
116984d9c625SLionel Sambuc 0, { 0, 0 }, { 0, 0 }, /* create time & date */
117084d9c625SLionel Sambuc { 0, 0 }, /* access date */
117184d9c625SLionel Sambuc { 0, 0 }, /* high bits of start cluster */
117284d9c625SLionel Sambuc { 210, 4 }, { 210, 4 }, /* modify time & date */
117384d9c625SLionel Sambuc { 0, 0 }, /* startcluster */
117484d9c625SLionel Sambuc { 0, 0, 0, 0 } /* filesize */
117584d9c625SLionel Sambuc }
117684d9c625SLionel Sambuc };
117784d9c625SLionel Sambuc
117884d9c625SLionel Sambuc int
msdosfs_mkdir(void * v)117984d9c625SLionel Sambuc msdosfs_mkdir(void *v)
118084d9c625SLionel Sambuc {
1181*0a6a1f1dSLionel Sambuc struct vop_mkdir_v3_args /* {
118284d9c625SLionel Sambuc struct vnode *a_dvp;
118384d9c625SLionel Sambuc struvt vnode **a_vpp;
118484d9c625SLionel Sambuc struvt componentname *a_cnp;
118584d9c625SLionel Sambuc struct vattr *a_vap;
118684d9c625SLionel Sambuc } */ *ap = v;
118784d9c625SLionel Sambuc struct componentname *cnp = ap->a_cnp;
118884d9c625SLionel Sambuc struct denode ndirent;
118984d9c625SLionel Sambuc struct denode *dep;
119084d9c625SLionel Sambuc struct denode *pdep = VTODE(ap->a_dvp);
119184d9c625SLionel Sambuc int error;
119284d9c625SLionel Sambuc int bn;
119384d9c625SLionel Sambuc u_long newcluster, pcl;
119484d9c625SLionel Sambuc daddr_t lbn;
119584d9c625SLionel Sambuc struct direntry *denp;
119684d9c625SLionel Sambuc struct msdosfsmount *pmp = pdep->de_pmp;
119784d9c625SLionel Sambuc struct buf *bp;
119884d9c625SLionel Sambuc int async = pdep->de_pmp->pm_mountp->mnt_flag & MNT_ASYNC;
119984d9c625SLionel Sambuc
120084d9c625SLionel Sambuc fstrans_start(ap->a_dvp->v_mount, FSTRANS_SHARED);
120184d9c625SLionel Sambuc /*
120284d9c625SLionel Sambuc * If this is the root directory and there is no space left we
120384d9c625SLionel Sambuc * can't do anything. This is because the root directory can not
120484d9c625SLionel Sambuc * change size.
120584d9c625SLionel Sambuc */
120684d9c625SLionel Sambuc if (pdep->de_StartCluster == MSDOSFSROOT
120784d9c625SLionel Sambuc && pdep->de_fndoffset >= pdep->de_FileSize) {
120884d9c625SLionel Sambuc error = ENOSPC;
120984d9c625SLionel Sambuc goto bad2;
121084d9c625SLionel Sambuc }
121184d9c625SLionel Sambuc
121284d9c625SLionel Sambuc /*
121384d9c625SLionel Sambuc * Allocate a cluster to hold the about to be created directory.
121484d9c625SLionel Sambuc */
121584d9c625SLionel Sambuc error = clusteralloc(pmp, 0, 1, &newcluster, NULL);
121684d9c625SLionel Sambuc if (error)
121784d9c625SLionel Sambuc goto bad2;
121884d9c625SLionel Sambuc
121984d9c625SLionel Sambuc memset(&ndirent, 0, sizeof(ndirent));
122084d9c625SLionel Sambuc ndirent.de_pmp = pmp;
122184d9c625SLionel Sambuc ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
122284d9c625SLionel Sambuc DETIMES(&ndirent, NULL, NULL, NULL, pmp->pm_gmtoff);
122384d9c625SLionel Sambuc
122484d9c625SLionel Sambuc /*
122584d9c625SLionel Sambuc * Now fill the cluster with the "." and ".." entries. And write
122684d9c625SLionel Sambuc * the cluster to disk. This way it is there for the parent
122784d9c625SLionel Sambuc * directory to be pointing at if there were a crash.
122884d9c625SLionel Sambuc */
122984d9c625SLionel Sambuc bn = cntobn(pmp, newcluster);
123084d9c625SLionel Sambuc lbn = de_bn2kb(pmp, bn);
123184d9c625SLionel Sambuc /* always succeeds */
123284d9c625SLionel Sambuc bp = getblk(pmp->pm_devvp, lbn, pmp->pm_bpcluster, 0, 0);
123384d9c625SLionel Sambuc memset(bp->b_data, 0, pmp->pm_bpcluster);
123484d9c625SLionel Sambuc memcpy(bp->b_data, &dosdirtemplate, sizeof dosdirtemplate);
123584d9c625SLionel Sambuc denp = (struct direntry *)bp->b_data;
123684d9c625SLionel Sambuc putushort(denp[0].deStartCluster, newcluster);
123784d9c625SLionel Sambuc putushort(denp[0].deCDate, ndirent.de_CDate);
123884d9c625SLionel Sambuc putushort(denp[0].deCTime, ndirent.de_CTime);
123984d9c625SLionel Sambuc denp[0].deCHundredth = ndirent.de_CHun;
124084d9c625SLionel Sambuc putushort(denp[0].deADate, ndirent.de_ADate);
124184d9c625SLionel Sambuc putushort(denp[0].deMDate, ndirent.de_MDate);
124284d9c625SLionel Sambuc putushort(denp[0].deMTime, ndirent.de_MTime);
124384d9c625SLionel Sambuc pcl = pdep->de_StartCluster;
124484d9c625SLionel Sambuc if (FAT32(pmp) && pcl == pmp->pm_rootdirblk)
124584d9c625SLionel Sambuc pcl = 0;
124684d9c625SLionel Sambuc putushort(denp[1].deStartCluster, pcl);
124784d9c625SLionel Sambuc putushort(denp[1].deCDate, ndirent.de_CDate);
124884d9c625SLionel Sambuc putushort(denp[1].deCTime, ndirent.de_CTime);
124984d9c625SLionel Sambuc denp[1].deCHundredth = ndirent.de_CHun;
125084d9c625SLionel Sambuc putushort(denp[1].deADate, ndirent.de_ADate);
125184d9c625SLionel Sambuc putushort(denp[1].deMDate, ndirent.de_MDate);
125284d9c625SLionel Sambuc putushort(denp[1].deMTime, ndirent.de_MTime);
125384d9c625SLionel Sambuc if (FAT32(pmp)) {
125484d9c625SLionel Sambuc putushort(denp[0].deHighClust, newcluster >> 16);
125584d9c625SLionel Sambuc putushort(denp[1].deHighClust, pdep->de_StartCluster >> 16);
125684d9c625SLionel Sambuc } else {
125784d9c625SLionel Sambuc putushort(denp[0].deHighClust, 0);
125884d9c625SLionel Sambuc putushort(denp[1].deHighClust, 0);
125984d9c625SLionel Sambuc }
126084d9c625SLionel Sambuc
126184d9c625SLionel Sambuc if (async)
126284d9c625SLionel Sambuc bdwrite(bp);
126384d9c625SLionel Sambuc else if ((error = bwrite(bp)) != 0)
126484d9c625SLionel Sambuc goto bad;
126584d9c625SLionel Sambuc
126684d9c625SLionel Sambuc /*
126784d9c625SLionel Sambuc * Now build up a directory entry pointing to the newly allocated
126884d9c625SLionel Sambuc * cluster. This will be written to an empty slot in the parent
126984d9c625SLionel Sambuc * directory.
127084d9c625SLionel Sambuc */
127184d9c625SLionel Sambuc if ((error = uniqdosname(pdep, cnp, ndirent.de_Name)) != 0)
127284d9c625SLionel Sambuc goto bad;
127384d9c625SLionel Sambuc
127484d9c625SLionel Sambuc ndirent.de_Attributes = ATTR_DIRECTORY;
127584d9c625SLionel Sambuc ndirent.de_StartCluster = newcluster;
127684d9c625SLionel Sambuc ndirent.de_FileSize = 0;
127784d9c625SLionel Sambuc ndirent.de_dev = pdep->de_dev;
127884d9c625SLionel Sambuc ndirent.de_devvp = pdep->de_devvp;
127984d9c625SLionel Sambuc if ((error = createde(&ndirent, pdep, &dep, cnp)) != 0)
128084d9c625SLionel Sambuc goto bad;
128184d9c625SLionel Sambuc VN_KNOTE(ap->a_dvp, NOTE_WRITE | NOTE_LINK);
128284d9c625SLionel Sambuc *ap->a_vpp = DETOV(dep);
128384d9c625SLionel Sambuc fstrans_done(ap->a_dvp->v_mount);
128484d9c625SLionel Sambuc return (0);
128584d9c625SLionel Sambuc
128684d9c625SLionel Sambuc bad:
128784d9c625SLionel Sambuc clusterfree(pmp, newcluster, NULL);
128884d9c625SLionel Sambuc bad2:
128984d9c625SLionel Sambuc fstrans_done(ap->a_dvp->v_mount);
129084d9c625SLionel Sambuc return (error);
129184d9c625SLionel Sambuc }
129284d9c625SLionel Sambuc
129384d9c625SLionel Sambuc int
msdosfs_rmdir(void * v)129484d9c625SLionel Sambuc msdosfs_rmdir(void *v)
129584d9c625SLionel Sambuc {
129684d9c625SLionel Sambuc struct vop_rmdir_args /* {
129784d9c625SLionel Sambuc struct vnode *a_dvp;
129884d9c625SLionel Sambuc struct vnode *a_vp;
129984d9c625SLionel Sambuc struct componentname *a_cnp;
130084d9c625SLionel Sambuc } */ *ap = v;
130184d9c625SLionel Sambuc struct vnode *vp = ap->a_vp;
130284d9c625SLionel Sambuc struct vnode *dvp = ap->a_dvp;
130384d9c625SLionel Sambuc struct mount *mp = dvp->v_mount;
130484d9c625SLionel Sambuc struct componentname *cnp = ap->a_cnp;
130584d9c625SLionel Sambuc struct denode *ip, *dp;
130684d9c625SLionel Sambuc int error;
130784d9c625SLionel Sambuc
130884d9c625SLionel Sambuc ip = VTODE(vp);
130984d9c625SLionel Sambuc dp = VTODE(dvp);
131084d9c625SLionel Sambuc /*
131184d9c625SLionel Sambuc * No rmdir "." please.
131284d9c625SLionel Sambuc */
131384d9c625SLionel Sambuc if (dp == ip) {
131484d9c625SLionel Sambuc vrele(dvp);
131584d9c625SLionel Sambuc vput(vp);
131684d9c625SLionel Sambuc return (EINVAL);
131784d9c625SLionel Sambuc }
131884d9c625SLionel Sambuc fstrans_start(mp, FSTRANS_SHARED);
131984d9c625SLionel Sambuc /*
132084d9c625SLionel Sambuc * Verify the directory is empty (and valid).
132184d9c625SLionel Sambuc * (Rmdir ".." won't be valid since
132284d9c625SLionel Sambuc * ".." will contain a reference to
132384d9c625SLionel Sambuc * the current directory and thus be
132484d9c625SLionel Sambuc * non-empty.)
132584d9c625SLionel Sambuc */
132684d9c625SLionel Sambuc error = 0;
132784d9c625SLionel Sambuc if (!dosdirempty(ip) || ip->de_flag & DE_RENAME) {
132884d9c625SLionel Sambuc error = ENOTEMPTY;
132984d9c625SLionel Sambuc goto out;
133084d9c625SLionel Sambuc }
133184d9c625SLionel Sambuc /*
133284d9c625SLionel Sambuc * Delete the entry from the directory. For dos filesystems this
133384d9c625SLionel Sambuc * gets rid of the directory entry on disk, the in memory copy
133484d9c625SLionel Sambuc * still exists but the de_refcnt is <= 0. This prevents it from
133584d9c625SLionel Sambuc * being found by deget(). When the vput() on dep is done we give
133684d9c625SLionel Sambuc * up access and eventually msdosfs_reclaim() will be called which
133784d9c625SLionel Sambuc * will remove it from the denode cache.
133884d9c625SLionel Sambuc */
133984d9c625SLionel Sambuc if ((error = removede(dp, ip)) != 0)
134084d9c625SLionel Sambuc goto out;
134184d9c625SLionel Sambuc /*
134284d9c625SLionel Sambuc * This is where we decrement the link count in the parent
134384d9c625SLionel Sambuc * directory. Since dos filesystems don't do this we just purge
134484d9c625SLionel Sambuc * the name cache and let go of the parent directory denode.
134584d9c625SLionel Sambuc */
134684d9c625SLionel Sambuc VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
134784d9c625SLionel Sambuc cache_purge(dvp);
134884d9c625SLionel Sambuc vput(dvp);
134984d9c625SLionel Sambuc dvp = NULL;
135084d9c625SLionel Sambuc /*
135184d9c625SLionel Sambuc * Truncate the directory that is being deleted.
135284d9c625SLionel Sambuc */
135384d9c625SLionel Sambuc error = detrunc(ip, (u_long)0, IO_SYNC, cnp->cn_cred);
135484d9c625SLionel Sambuc cache_purge(vp);
135584d9c625SLionel Sambuc out:
135684d9c625SLionel Sambuc VN_KNOTE(vp, NOTE_DELETE);
135784d9c625SLionel Sambuc if (dvp)
135884d9c625SLionel Sambuc vput(dvp);
135984d9c625SLionel Sambuc vput(vp);
136084d9c625SLionel Sambuc fstrans_done(mp);
136184d9c625SLionel Sambuc return (error);
136284d9c625SLionel Sambuc }
136384d9c625SLionel Sambuc
136484d9c625SLionel Sambuc int
msdosfs_readdir(void * v)136584d9c625SLionel Sambuc msdosfs_readdir(void *v)
136684d9c625SLionel Sambuc {
136784d9c625SLionel Sambuc struct vop_readdir_args /* {
136884d9c625SLionel Sambuc struct vnode *a_vp;
136984d9c625SLionel Sambuc struct uio *a_uio;
137084d9c625SLionel Sambuc kauth_cred_t a_cred;
137184d9c625SLionel Sambuc int *a_eofflag;
137284d9c625SLionel Sambuc off_t **a_cookies;
137384d9c625SLionel Sambuc int *a_ncookies;
137484d9c625SLionel Sambuc } */ *ap = v;
137584d9c625SLionel Sambuc int error = 0;
137684d9c625SLionel Sambuc int diff;
137784d9c625SLionel Sambuc long n;
137884d9c625SLionel Sambuc int blsize;
137984d9c625SLionel Sambuc long on;
138084d9c625SLionel Sambuc long lost;
138184d9c625SLionel Sambuc long count;
138284d9c625SLionel Sambuc u_long cn;
138384d9c625SLionel Sambuc ino_t fileno;
138484d9c625SLionel Sambuc u_long dirsperblk;
138584d9c625SLionel Sambuc long bias = 0;
138684d9c625SLionel Sambuc daddr_t bn, lbn;
138784d9c625SLionel Sambuc struct buf *bp;
138884d9c625SLionel Sambuc struct denode *dep = VTODE(ap->a_vp);
138984d9c625SLionel Sambuc struct msdosfsmount *pmp = dep->de_pmp;
139084d9c625SLionel Sambuc struct direntry *dentp;
139184d9c625SLionel Sambuc struct dirent *dirbuf;
139284d9c625SLionel Sambuc struct uio *uio = ap->a_uio;
139384d9c625SLionel Sambuc off_t *cookies = NULL;
139484d9c625SLionel Sambuc int ncookies = 0, nc = 0;
139584d9c625SLionel Sambuc off_t offset, uio_off;
139684d9c625SLionel Sambuc int chksum = -1;
139784d9c625SLionel Sambuc
139884d9c625SLionel Sambuc #ifdef MSDOSFS_DEBUG
139984d9c625SLionel Sambuc printf("msdosfs_readdir(): vp %p, uio %p, cred %p, eofflagp %p\n",
140084d9c625SLionel Sambuc ap->a_vp, uio, ap->a_cred, ap->a_eofflag);
140184d9c625SLionel Sambuc #endif
140284d9c625SLionel Sambuc
140384d9c625SLionel Sambuc /*
140484d9c625SLionel Sambuc * msdosfs_readdir() won't operate properly on regular files since
140584d9c625SLionel Sambuc * it does i/o only with the filesystem vnode, and hence can
140684d9c625SLionel Sambuc * retrieve the wrong block from the buffer cache for a plain file.
140784d9c625SLionel Sambuc * So, fail attempts to readdir() on a plain file.
140884d9c625SLionel Sambuc */
140984d9c625SLionel Sambuc if ((dep->de_Attributes & ATTR_DIRECTORY) == 0)
141084d9c625SLionel Sambuc return (ENOTDIR);
141184d9c625SLionel Sambuc
141284d9c625SLionel Sambuc /*
141384d9c625SLionel Sambuc * If the user buffer is smaller than the size of one dos directory
141484d9c625SLionel Sambuc * entry or the file offset is not a multiple of the size of a
141584d9c625SLionel Sambuc * directory entry, then we fail the read.
141684d9c625SLionel Sambuc */
141784d9c625SLionel Sambuc count = uio->uio_resid & ~(sizeof(struct direntry) - 1);
141884d9c625SLionel Sambuc offset = uio->uio_offset;
141984d9c625SLionel Sambuc if (count < sizeof(struct direntry) ||
142084d9c625SLionel Sambuc (offset & (sizeof(struct direntry) - 1)))
142184d9c625SLionel Sambuc return (EINVAL);
142284d9c625SLionel Sambuc lost = uio->uio_resid - count;
142384d9c625SLionel Sambuc uio->uio_resid = count;
142484d9c625SLionel Sambuc uio_off = uio->uio_offset;
142584d9c625SLionel Sambuc
142684d9c625SLionel Sambuc fstrans_start(ap->a_vp->v_mount, FSTRANS_SHARED);
142784d9c625SLionel Sambuc
142884d9c625SLionel Sambuc /* Allocate a temporary dirent buffer. */
142984d9c625SLionel Sambuc dirbuf = malloc(sizeof(struct dirent), M_MSDOSFSTMP, M_WAITOK | M_ZERO);
143084d9c625SLionel Sambuc
143184d9c625SLionel Sambuc if (ap->a_ncookies) {
143284d9c625SLionel Sambuc nc = uio->uio_resid / _DIRENT_MINSIZE((struct dirent *)0);
143384d9c625SLionel Sambuc cookies = malloc(nc * sizeof (off_t), M_TEMP, M_WAITOK);
143484d9c625SLionel Sambuc *ap->a_cookies = cookies;
143584d9c625SLionel Sambuc }
143684d9c625SLionel Sambuc
143784d9c625SLionel Sambuc dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry);
143884d9c625SLionel Sambuc
143984d9c625SLionel Sambuc /*
144084d9c625SLionel Sambuc * If they are reading from the root directory then, we simulate
144184d9c625SLionel Sambuc * the . and .. entries since these don't exist in the root
144284d9c625SLionel Sambuc * directory. We also set the offset bias to make up for having to
144384d9c625SLionel Sambuc * simulate these entries. By this I mean that at file offset 64 we
144484d9c625SLionel Sambuc * read the first entry in the root directory that lives on disk.
144584d9c625SLionel Sambuc */
144684d9c625SLionel Sambuc if (dep->de_StartCluster == MSDOSFSROOT
144784d9c625SLionel Sambuc || (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk)) {
144884d9c625SLionel Sambuc #if 0
144984d9c625SLionel Sambuc printf("msdosfs_readdir(): going after . or .. in root dir, "
145084d9c625SLionel Sambuc "offset %" PRIu64 "\n", offset);
145184d9c625SLionel Sambuc #endif
145284d9c625SLionel Sambuc bias = 2 * sizeof(struct direntry);
145384d9c625SLionel Sambuc if (offset < bias) {
145484d9c625SLionel Sambuc for (n = (int)offset / sizeof(struct direntry);
145584d9c625SLionel Sambuc n < 2; n++) {
145684d9c625SLionel Sambuc if (FAT32(pmp))
145784d9c625SLionel Sambuc dirbuf->d_fileno = cntobn(pmp,
145884d9c625SLionel Sambuc (ino_t)pmp->pm_rootdirblk)
145984d9c625SLionel Sambuc * dirsperblk;
146084d9c625SLionel Sambuc else
146184d9c625SLionel Sambuc dirbuf->d_fileno = 1;
146284d9c625SLionel Sambuc dirbuf->d_type = DT_DIR;
146384d9c625SLionel Sambuc switch (n) {
146484d9c625SLionel Sambuc case 0:
146584d9c625SLionel Sambuc dirbuf->d_namlen = 1;
146684d9c625SLionel Sambuc strlcpy(dirbuf->d_name, ".",
146784d9c625SLionel Sambuc sizeof(dirbuf->d_name));
146884d9c625SLionel Sambuc break;
146984d9c625SLionel Sambuc case 1:
147084d9c625SLionel Sambuc dirbuf->d_namlen = 2;
147184d9c625SLionel Sambuc strlcpy(dirbuf->d_name, "..",
147284d9c625SLionel Sambuc sizeof(dirbuf->d_name));
147384d9c625SLionel Sambuc break;
147484d9c625SLionel Sambuc }
147584d9c625SLionel Sambuc dirbuf->d_reclen = _DIRENT_SIZE(dirbuf);
147684d9c625SLionel Sambuc if (uio->uio_resid < dirbuf->d_reclen)
147784d9c625SLionel Sambuc goto out;
147884d9c625SLionel Sambuc error = uiomove(dirbuf, dirbuf->d_reclen, uio);
147984d9c625SLionel Sambuc if (error)
148084d9c625SLionel Sambuc goto out;
148184d9c625SLionel Sambuc offset += sizeof(struct direntry);
148284d9c625SLionel Sambuc uio_off = offset;
148384d9c625SLionel Sambuc if (cookies) {
148484d9c625SLionel Sambuc *cookies++ = offset;
148584d9c625SLionel Sambuc ncookies++;
148684d9c625SLionel Sambuc if (ncookies >= nc)
148784d9c625SLionel Sambuc goto out;
148884d9c625SLionel Sambuc }
148984d9c625SLionel Sambuc }
149084d9c625SLionel Sambuc }
149184d9c625SLionel Sambuc }
149284d9c625SLionel Sambuc
149384d9c625SLionel Sambuc while (uio->uio_resid > 0) {
149484d9c625SLionel Sambuc lbn = de_cluster(pmp, offset - bias);
149584d9c625SLionel Sambuc on = (offset - bias) & pmp->pm_crbomask;
149684d9c625SLionel Sambuc n = MIN(pmp->pm_bpcluster - on, uio->uio_resid);
149784d9c625SLionel Sambuc diff = dep->de_FileSize - (offset - bias);
149884d9c625SLionel Sambuc if (diff <= 0)
149984d9c625SLionel Sambuc break;
150084d9c625SLionel Sambuc n = MIN(n, diff);
150184d9c625SLionel Sambuc if ((error = pcbmap(dep, lbn, &bn, &cn, &blsize)) != 0)
150284d9c625SLionel Sambuc break;
150384d9c625SLionel Sambuc error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
1504*0a6a1f1dSLionel Sambuc 0, &bp);
150584d9c625SLionel Sambuc if (error) {
150684d9c625SLionel Sambuc goto bad;
150784d9c625SLionel Sambuc }
150884d9c625SLionel Sambuc n = MIN(n, blsize - bp->b_resid);
150984d9c625SLionel Sambuc
151084d9c625SLionel Sambuc /*
151184d9c625SLionel Sambuc * Convert from dos directory entries to fs-independent
151284d9c625SLionel Sambuc * directory entries.
151384d9c625SLionel Sambuc */
151484d9c625SLionel Sambuc for (dentp = (struct direntry *)((char *)bp->b_data + on);
151584d9c625SLionel Sambuc (char *)dentp < (char *)bp->b_data + on + n;
151684d9c625SLionel Sambuc dentp++, offset += sizeof(struct direntry)) {
151784d9c625SLionel Sambuc #if 0
151884d9c625SLionel Sambuc
151984d9c625SLionel Sambuc printf("rd: dentp %08x prev %08x crnt %08x deName %02x attr %02x\n",
152084d9c625SLionel Sambuc dentp, prev, crnt, dentp->deName[0], dentp->deAttributes);
152184d9c625SLionel Sambuc #endif
152284d9c625SLionel Sambuc /*
152384d9c625SLionel Sambuc * If this is an unused entry, we can stop.
152484d9c625SLionel Sambuc */
152584d9c625SLionel Sambuc if (dentp->deName[0] == SLOT_EMPTY) {
152684d9c625SLionel Sambuc brelse(bp, 0);
152784d9c625SLionel Sambuc goto out;
152884d9c625SLionel Sambuc }
152984d9c625SLionel Sambuc /*
153084d9c625SLionel Sambuc * Skip deleted entries.
153184d9c625SLionel Sambuc */
153284d9c625SLionel Sambuc if (dentp->deName[0] == SLOT_DELETED) {
153384d9c625SLionel Sambuc chksum = -1;
153484d9c625SLionel Sambuc continue;
153584d9c625SLionel Sambuc }
153684d9c625SLionel Sambuc
153784d9c625SLionel Sambuc /*
153884d9c625SLionel Sambuc * Handle Win95 long directory entries
153984d9c625SLionel Sambuc */
154084d9c625SLionel Sambuc if (dentp->deAttributes == ATTR_WIN95) {
154184d9c625SLionel Sambuc if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
154284d9c625SLionel Sambuc continue;
154384d9c625SLionel Sambuc chksum = win2unixfn((struct winentry *)dentp,
154484d9c625SLionel Sambuc dirbuf, chksum);
154584d9c625SLionel Sambuc continue;
154684d9c625SLionel Sambuc }
154784d9c625SLionel Sambuc
154884d9c625SLionel Sambuc /*
154984d9c625SLionel Sambuc * Skip volume labels
155084d9c625SLionel Sambuc */
155184d9c625SLionel Sambuc if (dentp->deAttributes & ATTR_VOLUME) {
155284d9c625SLionel Sambuc chksum = -1;
155384d9c625SLionel Sambuc continue;
155484d9c625SLionel Sambuc }
155584d9c625SLionel Sambuc /*
155684d9c625SLionel Sambuc * This computation of d_fileno must match
155784d9c625SLionel Sambuc * the computation of va_fileid in
155884d9c625SLionel Sambuc * msdosfs_getattr.
155984d9c625SLionel Sambuc */
156084d9c625SLionel Sambuc if (dentp->deAttributes & ATTR_DIRECTORY) {
156184d9c625SLionel Sambuc fileno = getushort(dentp->deStartCluster);
156284d9c625SLionel Sambuc if (FAT32(pmp))
156384d9c625SLionel Sambuc fileno |= ((ino_t)getushort(dentp->deHighClust)) << 16;
156484d9c625SLionel Sambuc /* if this is the root directory */
156584d9c625SLionel Sambuc if (fileno == MSDOSFSROOT)
156684d9c625SLionel Sambuc if (FAT32(pmp))
156784d9c625SLionel Sambuc fileno = cntobn(pmp,
156884d9c625SLionel Sambuc (ino_t)pmp->pm_rootdirblk)
156984d9c625SLionel Sambuc * dirsperblk;
157084d9c625SLionel Sambuc else
157184d9c625SLionel Sambuc fileno = 1;
157284d9c625SLionel Sambuc else
157384d9c625SLionel Sambuc fileno = cntobn(pmp, fileno) * dirsperblk;
157484d9c625SLionel Sambuc dirbuf->d_fileno = fileno;
157584d9c625SLionel Sambuc dirbuf->d_type = DT_DIR;
157684d9c625SLionel Sambuc } else {
157784d9c625SLionel Sambuc dirbuf->d_fileno =
157884d9c625SLionel Sambuc offset / sizeof(struct direntry);
157984d9c625SLionel Sambuc dirbuf->d_type = DT_REG;
158084d9c625SLionel Sambuc }
158184d9c625SLionel Sambuc if (chksum != winChksum(dentp->deName))
158284d9c625SLionel Sambuc dirbuf->d_namlen = dos2unixfn(dentp->deName,
158384d9c625SLionel Sambuc (u_char *)dirbuf->d_name,
158484d9c625SLionel Sambuc pmp->pm_flags & MSDOSFSMNT_SHORTNAME);
158584d9c625SLionel Sambuc else
158684d9c625SLionel Sambuc dirbuf->d_name[dirbuf->d_namlen] = 0;
158784d9c625SLionel Sambuc chksum = -1;
158884d9c625SLionel Sambuc dirbuf->d_reclen = _DIRENT_SIZE(dirbuf);
158984d9c625SLionel Sambuc if (uio->uio_resid < dirbuf->d_reclen) {
159084d9c625SLionel Sambuc brelse(bp, 0);
159184d9c625SLionel Sambuc goto out;
159284d9c625SLionel Sambuc }
159384d9c625SLionel Sambuc error = uiomove(dirbuf, dirbuf->d_reclen, uio);
159484d9c625SLionel Sambuc if (error) {
159584d9c625SLionel Sambuc brelse(bp, 0);
159684d9c625SLionel Sambuc goto out;
159784d9c625SLionel Sambuc }
159884d9c625SLionel Sambuc uio_off = offset + sizeof(struct direntry);
159984d9c625SLionel Sambuc if (cookies) {
160084d9c625SLionel Sambuc *cookies++ = offset + sizeof(struct direntry);
160184d9c625SLionel Sambuc ncookies++;
160284d9c625SLionel Sambuc if (ncookies >= nc) {
160384d9c625SLionel Sambuc brelse(bp, 0);
160484d9c625SLionel Sambuc goto out;
160584d9c625SLionel Sambuc }
160684d9c625SLionel Sambuc }
160784d9c625SLionel Sambuc }
160884d9c625SLionel Sambuc brelse(bp, 0);
160984d9c625SLionel Sambuc }
161084d9c625SLionel Sambuc
161184d9c625SLionel Sambuc out:
161284d9c625SLionel Sambuc uio->uio_offset = uio_off;
161384d9c625SLionel Sambuc uio->uio_resid += lost;
161484d9c625SLionel Sambuc if (dep->de_FileSize - (offset - bias) <= 0)
161584d9c625SLionel Sambuc *ap->a_eofflag = 1;
161684d9c625SLionel Sambuc else
161784d9c625SLionel Sambuc *ap->a_eofflag = 0;
161884d9c625SLionel Sambuc
161984d9c625SLionel Sambuc if (ap->a_ncookies) {
162084d9c625SLionel Sambuc if (error) {
162184d9c625SLionel Sambuc free(*ap->a_cookies, M_TEMP);
162284d9c625SLionel Sambuc *ap->a_ncookies = 0;
162384d9c625SLionel Sambuc *ap->a_cookies = NULL;
162484d9c625SLionel Sambuc } else
162584d9c625SLionel Sambuc *ap->a_ncookies = ncookies;
162684d9c625SLionel Sambuc }
162784d9c625SLionel Sambuc
162884d9c625SLionel Sambuc bad:
162984d9c625SLionel Sambuc free(dirbuf, M_MSDOSFSTMP);
163084d9c625SLionel Sambuc fstrans_done(ap->a_vp->v_mount);
163184d9c625SLionel Sambuc return (error);
163284d9c625SLionel Sambuc }
163384d9c625SLionel Sambuc
163484d9c625SLionel Sambuc /*
163584d9c625SLionel Sambuc * vp - address of vnode file the file
163684d9c625SLionel Sambuc * bn - which cluster we are interested in mapping to a filesystem block number.
163784d9c625SLionel Sambuc * vpp - returns the vnode for the block special file holding the filesystem
163884d9c625SLionel Sambuc * containing the file of interest
163984d9c625SLionel Sambuc * bnp - address of where to return the filesystem relative block number
164084d9c625SLionel Sambuc */
164184d9c625SLionel Sambuc int
msdosfs_bmap(void * v)164284d9c625SLionel Sambuc msdosfs_bmap(void *v)
164384d9c625SLionel Sambuc {
164484d9c625SLionel Sambuc struct vop_bmap_args /* {
164584d9c625SLionel Sambuc struct vnode *a_vp;
164684d9c625SLionel Sambuc daddr_t a_bn;
164784d9c625SLionel Sambuc struct vnode **a_vpp;
164884d9c625SLionel Sambuc daddr_t *a_bnp;
164984d9c625SLionel Sambuc int *a_runp;
165084d9c625SLionel Sambuc } */ *ap = v;
165184d9c625SLionel Sambuc struct denode *dep = VTODE(ap->a_vp);
165284d9c625SLionel Sambuc int run, maxrun;
165384d9c625SLionel Sambuc daddr_t runbn;
165484d9c625SLionel Sambuc int status;
165584d9c625SLionel Sambuc
165684d9c625SLionel Sambuc if (ap->a_vpp != NULL)
165784d9c625SLionel Sambuc *ap->a_vpp = dep->de_devvp;
165884d9c625SLionel Sambuc if (ap->a_bnp == NULL)
165984d9c625SLionel Sambuc return (0);
166084d9c625SLionel Sambuc status = pcbmap(dep, ap->a_bn, ap->a_bnp, 0, 0);
166184d9c625SLionel Sambuc
166284d9c625SLionel Sambuc /*
166384d9c625SLionel Sambuc * From FreeBSD:
166484d9c625SLionel Sambuc * A little kludgy, but we loop calling pcbmap until we
166584d9c625SLionel Sambuc * reach the end of the contiguous piece, or reach MAXPHYS.
166684d9c625SLionel Sambuc * Since it reduces disk I/Os, the "wasted" CPU is put to
166784d9c625SLionel Sambuc * good use (4 to 5 fold sequential read I/O improvement on USB
166884d9c625SLionel Sambuc * drives).
166984d9c625SLionel Sambuc */
167084d9c625SLionel Sambuc if (ap->a_runp != NULL) {
167184d9c625SLionel Sambuc /* taken from ufs_bmap */
167284d9c625SLionel Sambuc maxrun = ulmin(MAXPHYS / dep->de_pmp->pm_bpcluster - 1,
167384d9c625SLionel Sambuc dep->de_pmp->pm_maxcluster - ap->a_bn);
167484d9c625SLionel Sambuc for (run = 1; run <= maxrun; run++) {
167584d9c625SLionel Sambuc if (pcbmap(dep, ap->a_bn + run, &runbn, NULL, NULL)
167684d9c625SLionel Sambuc != 0 || runbn !=
167784d9c625SLionel Sambuc *ap->a_bnp + de_cn2bn(dep->de_pmp, run))
167884d9c625SLionel Sambuc break;
167984d9c625SLionel Sambuc }
168084d9c625SLionel Sambuc *ap->a_runp = run - 1;
168184d9c625SLionel Sambuc }
168284d9c625SLionel Sambuc
168384d9c625SLionel Sambuc /*
168484d9c625SLionel Sambuc * We need to scale *ap->a_bnp by sector_size/DEV_BSIZE
168584d9c625SLionel Sambuc */
168684d9c625SLionel Sambuc *ap->a_bnp = de_bn2kb(dep->de_pmp, *ap->a_bnp);
168784d9c625SLionel Sambuc return status;
168884d9c625SLionel Sambuc }
168984d9c625SLionel Sambuc
169084d9c625SLionel Sambuc int
msdosfs_strategy(void * v)169184d9c625SLionel Sambuc msdosfs_strategy(void *v)
169284d9c625SLionel Sambuc {
169384d9c625SLionel Sambuc struct vop_strategy_args /* {
169484d9c625SLionel Sambuc struct vnode *a_vp;
169584d9c625SLionel Sambuc struct buf *a_bp;
169684d9c625SLionel Sambuc } */ *ap = v;
169784d9c625SLionel Sambuc struct vnode *vp = ap->a_vp;
169884d9c625SLionel Sambuc struct buf *bp = ap->a_bp;
169984d9c625SLionel Sambuc struct denode *dep = VTODE(bp->b_vp);
170084d9c625SLionel Sambuc int error = 0;
170184d9c625SLionel Sambuc
170284d9c625SLionel Sambuc if (vp->v_type == VBLK || vp->v_type == VCHR)
170384d9c625SLionel Sambuc panic("msdosfs_strategy: spec");
170484d9c625SLionel Sambuc /*
170584d9c625SLionel Sambuc * If we don't already know the filesystem relative block number
170684d9c625SLionel Sambuc * then get it using pcbmap(). If pcbmap() returns the block
170784d9c625SLionel Sambuc * number as -1 then we've got a hole in the file. DOS filesystems
170884d9c625SLionel Sambuc * don't allow files with holes, so we shouldn't ever see this.
170984d9c625SLionel Sambuc */
171084d9c625SLionel Sambuc if (bp->b_blkno == bp->b_lblkno) {
171184d9c625SLionel Sambuc error = pcbmap(dep, de_bn2cn(dep->de_pmp, bp->b_lblkno),
171284d9c625SLionel Sambuc &bp->b_blkno, 0, 0);
171384d9c625SLionel Sambuc if (error)
171484d9c625SLionel Sambuc bp->b_blkno = -1;
171584d9c625SLionel Sambuc if (bp->b_blkno == -1)
171684d9c625SLionel Sambuc clrbuf(bp);
171784d9c625SLionel Sambuc else
171884d9c625SLionel Sambuc bp->b_blkno = de_bn2kb(dep->de_pmp, bp->b_blkno);
171984d9c625SLionel Sambuc }
172084d9c625SLionel Sambuc if (bp->b_blkno == -1) {
172184d9c625SLionel Sambuc biodone(bp);
172284d9c625SLionel Sambuc return (error);
172384d9c625SLionel Sambuc }
172484d9c625SLionel Sambuc
172584d9c625SLionel Sambuc /*
172684d9c625SLionel Sambuc * Read/write the block from/to the disk that contains the desired
172784d9c625SLionel Sambuc * file block.
172884d9c625SLionel Sambuc */
172984d9c625SLionel Sambuc
173084d9c625SLionel Sambuc vp = dep->de_devvp;
173184d9c625SLionel Sambuc return (VOP_STRATEGY(vp, bp));
173284d9c625SLionel Sambuc }
173384d9c625SLionel Sambuc
173484d9c625SLionel Sambuc int
msdosfs_print(void * v)173584d9c625SLionel Sambuc msdosfs_print(void *v)
173684d9c625SLionel Sambuc {
173784d9c625SLionel Sambuc struct vop_print_args /* {
173884d9c625SLionel Sambuc struct vnode *vp;
173984d9c625SLionel Sambuc } */ *ap = v;
174084d9c625SLionel Sambuc struct denode *dep = VTODE(ap->a_vp);
174184d9c625SLionel Sambuc
174284d9c625SLionel Sambuc printf(
174384d9c625SLionel Sambuc "tag VT_MSDOSFS, startcluster %ld, dircluster %ld, diroffset %ld ",
174484d9c625SLionel Sambuc dep->de_StartCluster, dep->de_dirclust, dep->de_diroffset);
174584d9c625SLionel Sambuc printf(" dev %llu, %llu ", (unsigned long long)major(dep->de_dev),
174684d9c625SLionel Sambuc (unsigned long long)minor(dep->de_dev));
174784d9c625SLionel Sambuc printf("\n");
174884d9c625SLionel Sambuc return (0);
174984d9c625SLionel Sambuc }
175084d9c625SLionel Sambuc
175184d9c625SLionel Sambuc int
msdosfs_advlock(void * v)175284d9c625SLionel Sambuc msdosfs_advlock(void *v)
175384d9c625SLionel Sambuc {
175484d9c625SLionel Sambuc struct vop_advlock_args /* {
175584d9c625SLionel Sambuc struct vnode *a_vp;
175684d9c625SLionel Sambuc void *a_id;
175784d9c625SLionel Sambuc int a_op;
175884d9c625SLionel Sambuc struct flock *a_fl;
175984d9c625SLionel Sambuc int a_flags;
176084d9c625SLionel Sambuc } */ *ap = v;
176184d9c625SLionel Sambuc struct denode *dep = VTODE(ap->a_vp);
176284d9c625SLionel Sambuc
176384d9c625SLionel Sambuc return lf_advlock(ap, &dep->de_lockf, dep->de_FileSize);
176484d9c625SLionel Sambuc }
176584d9c625SLionel Sambuc
176684d9c625SLionel Sambuc int
msdosfs_pathconf(void * v)176784d9c625SLionel Sambuc msdosfs_pathconf(void *v)
176884d9c625SLionel Sambuc {
176984d9c625SLionel Sambuc struct vop_pathconf_args /* {
177084d9c625SLionel Sambuc struct vnode *a_vp;
177184d9c625SLionel Sambuc int a_name;
177284d9c625SLionel Sambuc register_t *a_retval;
177384d9c625SLionel Sambuc } */ *ap = v;
177484d9c625SLionel Sambuc
177584d9c625SLionel Sambuc switch (ap->a_name) {
177684d9c625SLionel Sambuc case _PC_LINK_MAX:
177784d9c625SLionel Sambuc *ap->a_retval = 1;
177884d9c625SLionel Sambuc return (0);
177984d9c625SLionel Sambuc case _PC_NAME_MAX:
178084d9c625SLionel Sambuc *ap->a_retval = ap->a_vp->v_mount->mnt_stat.f_namemax;
178184d9c625SLionel Sambuc return (0);
178284d9c625SLionel Sambuc case _PC_PATH_MAX:
178384d9c625SLionel Sambuc *ap->a_retval = PATH_MAX;
178484d9c625SLionel Sambuc return (0);
178584d9c625SLionel Sambuc case _PC_CHOWN_RESTRICTED:
178684d9c625SLionel Sambuc *ap->a_retval = 1;
178784d9c625SLionel Sambuc return (0);
178884d9c625SLionel Sambuc case _PC_NO_TRUNC:
178984d9c625SLionel Sambuc *ap->a_retval = 1;
179084d9c625SLionel Sambuc return (0);
179184d9c625SLionel Sambuc case _PC_SYNC_IO:
179284d9c625SLionel Sambuc *ap->a_retval = 1;
179384d9c625SLionel Sambuc return (0);
179484d9c625SLionel Sambuc case _PC_FILESIZEBITS:
179584d9c625SLionel Sambuc *ap->a_retval = 32;
179684d9c625SLionel Sambuc return (0);
179784d9c625SLionel Sambuc default:
179884d9c625SLionel Sambuc return (EINVAL);
179984d9c625SLionel Sambuc }
180084d9c625SLionel Sambuc /* NOTREACHED */
180184d9c625SLionel Sambuc }
180284d9c625SLionel Sambuc
180384d9c625SLionel Sambuc int
msdosfs_fsync(void * v)180484d9c625SLionel Sambuc msdosfs_fsync(void *v)
180584d9c625SLionel Sambuc {
180684d9c625SLionel Sambuc struct vop_fsync_args /* {
180784d9c625SLionel Sambuc struct vnode *a_vp;
180884d9c625SLionel Sambuc kauth_cred_t a_cred;
180984d9c625SLionel Sambuc int a_flags;
181084d9c625SLionel Sambuc off_t offlo;
181184d9c625SLionel Sambuc off_t offhi;
181284d9c625SLionel Sambuc } */ *ap = v;
181384d9c625SLionel Sambuc struct vnode *vp = ap->a_vp;
181484d9c625SLionel Sambuc int wait;
181584d9c625SLionel Sambuc int error;
181684d9c625SLionel Sambuc
181784d9c625SLionel Sambuc fstrans_start(vp->v_mount, FSTRANS_LAZY);
181884d9c625SLionel Sambuc wait = (ap->a_flags & FSYNC_WAIT) != 0;
181984d9c625SLionel Sambuc error = vflushbuf(vp, ap->a_flags);
182084d9c625SLionel Sambuc if (error == 0 && (ap->a_flags & FSYNC_DATAONLY) == 0)
182184d9c625SLionel Sambuc error = msdosfs_update(vp, NULL, NULL, wait ? UPDATE_WAIT : 0);
182284d9c625SLionel Sambuc
182384d9c625SLionel Sambuc if (error == 0 && ap->a_flags & FSYNC_CACHE) {
182484d9c625SLionel Sambuc struct denode *dep = VTODE(vp);
182584d9c625SLionel Sambuc struct vnode *devvp = dep->de_devvp;
182684d9c625SLionel Sambuc
182784d9c625SLionel Sambuc int l = 0;
182884d9c625SLionel Sambuc error = VOP_IOCTL(devvp, DIOCCACHESYNC, &l, FWRITE,
182984d9c625SLionel Sambuc curlwp->l_cred);
183084d9c625SLionel Sambuc }
183184d9c625SLionel Sambuc fstrans_done(vp->v_mount);
183284d9c625SLionel Sambuc
183384d9c625SLionel Sambuc return (error);
183484d9c625SLionel Sambuc }
183584d9c625SLionel Sambuc
183684d9c625SLionel Sambuc void
msdosfs_detimes(struct denode * dep,const struct timespec * acc,const struct timespec * mod,const struct timespec * cre,int gmtoff)183784d9c625SLionel Sambuc msdosfs_detimes(struct denode *dep, const struct timespec *acc,
183884d9c625SLionel Sambuc const struct timespec *mod, const struct timespec *cre, int gmtoff)
183984d9c625SLionel Sambuc {
184084d9c625SLionel Sambuc struct timespec *ts = NULL, tsb;
184184d9c625SLionel Sambuc
184284d9c625SLionel Sambuc KASSERT(dep->de_flag & (DE_UPDATE | DE_CREATE | DE_ACCESS));
184384d9c625SLionel Sambuc /* XXX just call getnanotime early and use result if needed? */
184484d9c625SLionel Sambuc dep->de_flag |= DE_MODIFIED;
184584d9c625SLionel Sambuc if (dep->de_flag & DE_UPDATE) {
184684d9c625SLionel Sambuc if (mod == NULL) {
184784d9c625SLionel Sambuc getnanotime(&tsb);
184884d9c625SLionel Sambuc mod = ts = &tsb;
184984d9c625SLionel Sambuc }
185084d9c625SLionel Sambuc unix2dostime(mod, gmtoff, &dep->de_MDate, &dep->de_MTime, NULL);
185184d9c625SLionel Sambuc dep->de_Attributes |= ATTR_ARCHIVE;
185284d9c625SLionel Sambuc }
185384d9c625SLionel Sambuc if ((dep->de_pmp->pm_flags & MSDOSFSMNT_NOWIN95) == 0) {
185484d9c625SLionel Sambuc if (dep->de_flag & DE_ACCESS) {
185584d9c625SLionel Sambuc if (acc == NULL)
185684d9c625SLionel Sambuc acc = ts == NULL ?
185784d9c625SLionel Sambuc (getnanotime(&tsb), ts = &tsb) : ts;
185884d9c625SLionel Sambuc unix2dostime(acc, gmtoff, &dep->de_ADate, NULL, NULL);
185984d9c625SLionel Sambuc }
186084d9c625SLionel Sambuc if (dep->de_flag & DE_CREATE) {
186184d9c625SLionel Sambuc if (cre == NULL)
186284d9c625SLionel Sambuc cre = ts == NULL ?
186384d9c625SLionel Sambuc (getnanotime(&tsb), ts = &tsb) : ts;
186484d9c625SLionel Sambuc unix2dostime(cre, gmtoff, &dep->de_CDate,
186584d9c625SLionel Sambuc &dep->de_CTime, &dep->de_CHun);
186684d9c625SLionel Sambuc }
186784d9c625SLionel Sambuc }
186884d9c625SLionel Sambuc
186984d9c625SLionel Sambuc dep->de_flag &= ~(DE_UPDATE | DE_CREATE | DE_ACCESS);
187084d9c625SLionel Sambuc }
187184d9c625SLionel Sambuc
187284d9c625SLionel Sambuc /* Global vfs data structures for msdosfs */
187384d9c625SLionel Sambuc int (**msdosfs_vnodeop_p)(void *);
187484d9c625SLionel Sambuc const struct vnodeopv_entry_desc msdosfs_vnodeop_entries[] = {
187584d9c625SLionel Sambuc { &vop_default_desc, vn_default_error },
187684d9c625SLionel Sambuc { &vop_lookup_desc, msdosfs_lookup }, /* lookup */
187784d9c625SLionel Sambuc { &vop_create_desc, msdosfs_create }, /* create */
187884d9c625SLionel Sambuc { &vop_mknod_desc, genfs_eopnotsupp }, /* mknod */
187984d9c625SLionel Sambuc { &vop_open_desc, genfs_nullop }, /* open */
188084d9c625SLionel Sambuc { &vop_close_desc, msdosfs_close }, /* close */
188184d9c625SLionel Sambuc { &vop_access_desc, msdosfs_access }, /* access */
188284d9c625SLionel Sambuc { &vop_getattr_desc, msdosfs_getattr }, /* getattr */
188384d9c625SLionel Sambuc { &vop_setattr_desc, msdosfs_setattr }, /* setattr */
188484d9c625SLionel Sambuc { &vop_read_desc, msdosfs_read }, /* read */
188584d9c625SLionel Sambuc { &vop_write_desc, msdosfs_write }, /* write */
1886*0a6a1f1dSLionel Sambuc { &vop_fallocate_desc, genfs_eopnotsupp }, /* fallocate */
1887*0a6a1f1dSLionel Sambuc { &vop_fdiscard_desc, genfs_eopnotsupp }, /* fdiscard */
188884d9c625SLionel Sambuc { &vop_fcntl_desc, genfs_fcntl }, /* fcntl */
188984d9c625SLionel Sambuc { &vop_ioctl_desc, msdosfs_ioctl }, /* ioctl */
189084d9c625SLionel Sambuc { &vop_poll_desc, msdosfs_poll }, /* poll */
189184d9c625SLionel Sambuc { &vop_kqfilter_desc, genfs_kqfilter }, /* kqfilter */
189284d9c625SLionel Sambuc { &vop_revoke_desc, msdosfs_revoke }, /* revoke */
189384d9c625SLionel Sambuc { &vop_mmap_desc, msdosfs_mmap }, /* mmap */
189484d9c625SLionel Sambuc { &vop_fsync_desc, msdosfs_fsync }, /* fsync */
189584d9c625SLionel Sambuc { &vop_seek_desc, msdosfs_seek }, /* seek */
189684d9c625SLionel Sambuc { &vop_remove_desc, msdosfs_remove }, /* remove */
189784d9c625SLionel Sambuc { &vop_link_desc, genfs_eopnotsupp }, /* link */
189884d9c625SLionel Sambuc { &vop_rename_desc, msdosfs_rename }, /* rename */
189984d9c625SLionel Sambuc { &vop_mkdir_desc, msdosfs_mkdir }, /* mkdir */
190084d9c625SLionel Sambuc { &vop_rmdir_desc, msdosfs_rmdir }, /* rmdir */
190184d9c625SLionel Sambuc { &vop_symlink_desc, genfs_eopnotsupp }, /* symlink */
190284d9c625SLionel Sambuc { &vop_readdir_desc, msdosfs_readdir }, /* readdir */
190384d9c625SLionel Sambuc { &vop_readlink_desc, genfs_einval }, /* readlink */
190484d9c625SLionel Sambuc { &vop_abortop_desc, msdosfs_abortop }, /* abortop */
190584d9c625SLionel Sambuc { &vop_inactive_desc, msdosfs_inactive }, /* inactive */
190684d9c625SLionel Sambuc { &vop_reclaim_desc, msdosfs_reclaim }, /* reclaim */
190784d9c625SLionel Sambuc { &vop_lock_desc, genfs_lock }, /* lock */
190884d9c625SLionel Sambuc { &vop_unlock_desc, genfs_unlock }, /* unlock */
190984d9c625SLionel Sambuc { &vop_bmap_desc, msdosfs_bmap }, /* bmap */
191084d9c625SLionel Sambuc { &vop_strategy_desc, msdosfs_strategy }, /* strategy */
191184d9c625SLionel Sambuc { &vop_print_desc, msdosfs_print }, /* print */
191284d9c625SLionel Sambuc { &vop_islocked_desc, genfs_islocked }, /* islocked */
191384d9c625SLionel Sambuc { &vop_pathconf_desc, msdosfs_pathconf }, /* pathconf */
191484d9c625SLionel Sambuc { &vop_advlock_desc, msdosfs_advlock }, /* advlock */
191584d9c625SLionel Sambuc { &vop_bwrite_desc, vn_bwrite }, /* bwrite */
191684d9c625SLionel Sambuc { &vop_getpages_desc, genfs_getpages }, /* getpages */
191784d9c625SLionel Sambuc { &vop_putpages_desc, genfs_putpages }, /* putpages */
191884d9c625SLionel Sambuc { NULL, NULL }
191984d9c625SLionel Sambuc };
192084d9c625SLionel Sambuc const struct vnodeopv_desc msdosfs_vnodeop_opv_desc =
192184d9c625SLionel Sambuc { &msdosfs_vnodeop_p, msdosfs_vnodeop_entries };
1922