150477Speter/* $FreeBSD$ */ 233548Sjkh/* $NetBSD: msdosfs_vnops.c,v 1.68 1998/02/10 14:10:04 mrg Exp $ */ 32893Sdfr 42893Sdfr/*- 533548Sjkh * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. 633548Sjkh * Copyright (C) 1994, 1995, 1997 TooLs GmbH. 72893Sdfr * All rights reserved. 82893Sdfr * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). 92893Sdfr * 102893Sdfr * Redistribution and use in source and binary forms, with or without 112893Sdfr * modification, are permitted provided that the following conditions 122893Sdfr * are met: 132893Sdfr * 1. Redistributions of source code must retain the above copyright 142893Sdfr * notice, this list of conditions and the following disclaimer. 152893Sdfr * 2. Redistributions in binary form must reproduce the above copyright 162893Sdfr * notice, this list of conditions and the following disclaimer in the 172893Sdfr * documentation and/or other materials provided with the distribution. 182893Sdfr * 3. All advertising materials mentioning features or use of this software 192893Sdfr * must display the following acknowledgement: 202893Sdfr * This product includes software developed by TooLs GmbH. 212893Sdfr * 4. The name of TooLs GmbH may not be used to endorse or promote products 222893Sdfr * derived from this software without specific prior written permission. 232893Sdfr * 242893Sdfr * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 252893Sdfr * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 262893Sdfr * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 272893Sdfr * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 282893Sdfr * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 292893Sdfr * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 302893Sdfr * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 312893Sdfr * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 322893Sdfr * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 332893Sdfr * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 342893Sdfr */ 35139776Simp/*- 362893Sdfr * Written by Paul Popelka (paulp@uts.amdahl.com) 378876Srgrimes * 382893Sdfr * You can do anything you want with this software, just don't say you wrote 392893Sdfr * it, and don't remove this notice. 408876Srgrimes * 412893Sdfr * This software is provided "as is". 428876Srgrimes * 432893Sdfr * The author supplies this software to be publicly redistributed on the 442893Sdfr * understanding that the author is not responsible for the correct 452893Sdfr * functioning of this software in any circumstances and is not liable for 462893Sdfr * any damages caused by this software. 478876Srgrimes * 482893Sdfr * October 1992 492893Sdfr */ 502893Sdfr 512893Sdfr#include <sys/param.h> 522893Sdfr#include <sys/systm.h> 53171755Sbde#include <sys/bio.h> 54171755Sbde#include <sys/buf.h> 55171755Sbde#include <sys/clock.h> 56171755Sbde#include <sys/dirent.h> 57171749Sbde#include <sys/lock.h> 58123964Sbde#include <sys/lockf.h> 59171755Sbde#include <sys/malloc.h> 60171755Sbde#include <sys/mount.h> 61171749Sbde#include <sys/mutex.h> 622893Sdfr#include <sys/namei.h> 63164033Srwatson#include <sys/priv.h> 64171755Sbde#include <sys/stat.h> 6518020Sbde#include <sys/unistd.h> 662893Sdfr#include <sys/vnode.h> 672893Sdfr 683152Sphk#include <vm/vm.h> 6912662Sdg#include <vm/vm_extern.h> 703152Sphk 7177162Sru#include <fs/msdosfs/bpb.h> 7277162Sru#include <fs/msdosfs/direntry.h> 7377162Sru#include <fs/msdosfs/denode.h> 7477162Sru#include <fs/msdosfs/fat.h> 75171755Sbde#include <fs/msdosfs/msdosfsmount.h> 7611921Sphk 7755308Sbp#define DOS_FILESIZE_MAX 0xffffffff 7855308Sbp 792893Sdfr/* 8011921Sphk * Prototypes for MSDOSFS vnode operations 8111921Sphk */ 82138270Sphkstatic vop_create_t msdosfs_create; 83138270Sphkstatic vop_mknod_t msdosfs_mknod; 84140965Speadarstatic vop_open_t msdosfs_open; 85138270Sphkstatic vop_close_t msdosfs_close; 86138270Sphkstatic vop_access_t msdosfs_access; 87138270Sphkstatic vop_getattr_t msdosfs_getattr; 88138270Sphkstatic vop_setattr_t msdosfs_setattr; 89138270Sphkstatic vop_read_t msdosfs_read; 90138270Sphkstatic vop_write_t msdosfs_write; 91138270Sphkstatic vop_fsync_t msdosfs_fsync; 92138270Sphkstatic vop_remove_t msdosfs_remove; 93138270Sphkstatic vop_link_t msdosfs_link; 94138270Sphkstatic vop_rename_t msdosfs_rename; 95138270Sphkstatic vop_mkdir_t msdosfs_mkdir; 96138270Sphkstatic vop_rmdir_t msdosfs_rmdir; 97138270Sphkstatic vop_symlink_t msdosfs_symlink; 98138270Sphkstatic vop_readdir_t msdosfs_readdir; 99138270Sphkstatic vop_bmap_t msdosfs_bmap; 100138270Sphkstatic vop_strategy_t msdosfs_strategy; 101138270Sphkstatic vop_print_t msdosfs_print; 102138270Sphkstatic vop_pathconf_t msdosfs_pathconf; 103166774Spjdstatic vop_vptofh_t msdosfs_vptofh; 10411921Sphk 10511921Sphk/* 1062893Sdfr * Some general notes: 1078876Srgrimes * 1082893Sdfr * In the ufs filesystem the inodes, superblocks, and indirect blocks are 1092893Sdfr * read/written using the vnode for the filesystem. Blocks that represent 1102893Sdfr * the contents of a file are read/written using the vnode for the file 1112893Sdfr * (including directories when they are read/written as files). This 1122893Sdfr * presents problems for the dos filesystem because data that should be in 1132893Sdfr * an inode (if dos had them) resides in the directory itself. Since we 1142893Sdfr * must update directory entries without the benefit of having the vnode 1152893Sdfr * for the directory we must use the vnode for the filesystem. This means 1162893Sdfr * that when a directory is actually read/written (via read, write, or 1172893Sdfr * readdir, or seek) we must use the vnode for the filesystem instead of 1182893Sdfr * the vnode for the directory as would happen in ufs. This is to insure we 1192893Sdfr * retreive the correct block from the buffer cache since the hash value is 1202893Sdfr * based upon the vnode address and the desired block number. 1212893Sdfr */ 1222893Sdfr 1232893Sdfr/* 1242893Sdfr * Create a regular file. On entry the directory to contain the file being 1252893Sdfr * created is locked. We must release before we return. We must also free 1262893Sdfr * the pathname buffer pointed at by cnp->cn_pnbuf, always on error, or 1272893Sdfr * only if the SAVESTART bit in cn_flags is clear on success. 1282893Sdfr */ 12911921Sphkstatic int 1302893Sdfrmsdosfs_create(ap) 1312893Sdfr struct vop_create_args /* { 1322893Sdfr struct vnode *a_dvp; 1332893Sdfr struct vnode **a_vpp; 1342893Sdfr struct componentname *a_cnp; 1352893Sdfr struct vattr *a_vap; 1362893Sdfr } */ *ap; 1372893Sdfr{ 1382893Sdfr struct componentname *cnp = ap->a_cnp; 1392893Sdfr struct denode ndirent; 1402893Sdfr struct denode *dep; 1412893Sdfr struct denode *pdep = VTODE(ap->a_dvp); 1422893Sdfr struct timespec ts; 1432893Sdfr int error; 1442893Sdfr 1452893Sdfr#ifdef MSDOSFS_DEBUG 14633548Sjkh printf("msdosfs_create(cnp %p, vap %p\n", cnp, ap->a_vap); 1472893Sdfr#endif 1482893Sdfr 1492893Sdfr /* 15033548Sjkh * If this is the root directory and there is no space left we 15133548Sjkh * can't do anything. This is because the root directory can not 15233548Sjkh * change size. 15333548Sjkh */ 15433548Sjkh if (pdep->de_StartCluster == MSDOSFSROOT 15533548Sjkh && pdep->de_fndoffset >= pdep->de_FileSize) { 15633548Sjkh error = ENOSPC; 15733548Sjkh goto bad; 15833548Sjkh } 15933548Sjkh 16033548Sjkh /* 1612893Sdfr * Create a directory entry for the file, then call createde() to 1622893Sdfr * have it installed. NOTE: DOS files are always executable. We 1632893Sdfr * use the absence of the owner write bit to make the file 1642893Sdfr * readonly. 1652893Sdfr */ 1662893Sdfr#ifdef DIAGNOSTIC 16733548Sjkh if ((cnp->cn_flags & HASBUF) == 0) 1682893Sdfr panic("msdosfs_create: no name"); 1692893Sdfr#endif 1702893Sdfr bzero(&ndirent, sizeof(ndirent)); 17133548Sjkh error = uniqdosname(pdep, cnp, ndirent.de_Name); 17233548Sjkh if (error) 17333548Sjkh goto bad; 17433548Sjkh 175254627Sken ndirent.de_Attributes = ATTR_ARCHIVE; 17641275Sdt ndirent.de_LowerCase = 0; 1772893Sdfr ndirent.de_StartCluster = 0; 1782893Sdfr ndirent.de_FileSize = 0; 17933548Sjkh ndirent.de_pmp = pdep->de_pmp; 18033548Sjkh ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE; 18134901Sphk getnanotime(&ts); 18233548Sjkh DETIMES(&ndirent, &ts, &ts, &ts); 18333548Sjkh error = createde(&ndirent, pdep, &dep, cnp); 18433548Sjkh if (error) 18533548Sjkh goto bad; 18633548Sjkh *ap->a_vpp = DETOV(dep); 18733548Sjkh return (0); 18833548Sjkh 18933548Sjkhbad: 19033548Sjkh return (error); 1912893Sdfr} 1922893Sdfr 19311921Sphkstatic int 1942893Sdfrmsdosfs_mknod(ap) 1952893Sdfr struct vop_mknod_args /* { 1962893Sdfr struct vnode *a_dvp; 1972893Sdfr struct vnode **a_vpp; 1982893Sdfr struct componentname *a_cnp; 1992893Sdfr struct vattr *a_vap; 2002893Sdfr } */ *ap; 2012893Sdfr{ 202171758Sbde 20379996Sassar return (EINVAL); 2042893Sdfr} 2052893Sdfr 20611921Sphkstatic int 207140965Speadarmsdosfs_open(ap) 208140965Speadar struct vop_open_args /* { 209140965Speadar struct vnode *a_vp; 210140965Speadar int a_mode; 211140965Speadar struct ucred *a_cred; 212140965Speadar struct thread *a_td; 213193924Skib struct file *a_fp; 214140965Speadar } */ *ap; 215140965Speadar{ 216140965Speadar struct denode *dep = VTODE(ap->a_vp); 217140965Speadar vnode_create_vobject(ap->a_vp, dep->de_FileSize, ap->a_td); 218140965Speadar return 0; 219140965Speadar} 220140965Speadar 221140965Speadarstatic int 2222893Sdfrmsdosfs_close(ap) 2232893Sdfr struct vop_close_args /* { 2242893Sdfr struct vnode *a_vp; 2252893Sdfr int a_fflag; 2262893Sdfr struct ucred *a_cred; 22783366Sjulian struct thread *a_td; 2282893Sdfr } */ *ap; 2292893Sdfr{ 2302893Sdfr struct vnode *vp = ap->a_vp; 2312893Sdfr struct denode *dep = VTODE(vp); 23233548Sjkh struct timespec ts; 2332893Sdfr 234103936Sjeff VI_LOCK(vp); 23533548Sjkh if (vp->v_usecount > 1) { 23634901Sphk getnanotime(&ts); 23733548Sjkh DETIMES(dep, &ts, &ts, &ts); 23833548Sjkh } 239103936Sjeff VI_UNLOCK(vp); 2402893Sdfr return 0; 2412893Sdfr} 2422893Sdfr 24311921Sphkstatic int 24411644Sdgmsdosfs_access(ap) 24511644Sdg struct vop_access_args /* { 24611644Sdg struct vnode *a_vp; 247184413Strasz accmode_t a_accmode; 24811644Sdg struct ucred *a_cred; 24983366Sjulian struct thread *a_td; 25011644Sdg } */ *ap; 2512893Sdfr{ 25211644Sdg struct vnode *vp = ap->a_vp; 25311644Sdg struct denode *dep = VTODE(ap->a_vp); 25411644Sdg struct msdosfsmount *pmp = dep->de_pmp; 255184413Strasz mode_t file_mode; 256184413Strasz accmode_t accmode = ap->a_accmode; 2578876Srgrimes 258254627Sken file_mode = S_IRWXU|S_IRWXG|S_IRWXO; 259118837Strhodes file_mode &= (vp->v_type == VDIR ? pmp->pm_dirmask : pmp->pm_mask); 26011644Sdg 26111644Sdg /* 262171771Sbde * Disallow writing to directories and regular files if the 263171771Sbde * filesystem is read-only. 26411644Sdg */ 265184413Strasz if (accmode & VWRITE) { 26611644Sdg switch (vp->v_type) { 267254627Sken case VREG: 26811644Sdg case VDIR: 26911644Sdg if (vp->v_mount->mnt_flag & MNT_RDONLY) 27011644Sdg return (EROFS); 27111644Sdg break; 27243305Sdillon default: 27343305Sdillon break; 27411644Sdg } 27511644Sdg } 27611644Sdg 27764865Sphk return (vaccess(vp->v_type, file_mode, pmp->pm_uid, pmp->pm_gid, 278184413Strasz ap->a_accmode, ap->a_cred, NULL)); 2792893Sdfr} 2802893Sdfr 28111921Sphkstatic int 2822893Sdfrmsdosfs_getattr(ap) 2832893Sdfr struct vop_getattr_args /* { 2842893Sdfr struct vnode *a_vp; 2852893Sdfr struct vattr *a_vap; 2862893Sdfr struct ucred *a_cred; 2872893Sdfr } */ *ap; 2882893Sdfr{ 2892893Sdfr struct denode *dep = VTODE(ap->a_vp); 29033548Sjkh struct msdosfsmount *pmp = dep->de_pmp; 2912893Sdfr struct vattr *vap = ap->a_vap; 29233548Sjkh mode_t mode; 29333548Sjkh struct timespec ts; 29433548Sjkh u_long dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry); 295131523Stjr uint64_t fileid; 2962893Sdfr 29734901Sphk getnanotime(&ts); 29833548Sjkh DETIMES(dep, &ts, &ts, &ts); 299189120Sjhb vap->va_fsid = dev2udev(pmp->pm_dev); 3002893Sdfr /* 3012893Sdfr * The following computation of the fileid must be the same as that 3022893Sdfr * used in msdosfs_readdir() to compute d_fileno. If not, pwd 3032893Sdfr * doesn't work. 3042893Sdfr */ 3052893Sdfr if (dep->de_Attributes & ATTR_DIRECTORY) { 306131523Stjr fileid = (uint64_t)cntobn(pmp, dep->de_StartCluster) * 307131523Stjr dirsperblk; 30833548Sjkh if (dep->de_StartCluster == MSDOSFSROOT) 30933548Sjkh fileid = 1; 3102893Sdfr } else { 311131523Stjr fileid = (uint64_t)cntobn(pmp, dep->de_dirclust) * 312131523Stjr dirsperblk; 31333548Sjkh if (dep->de_dirclust == MSDOSFSROOT) 314131523Stjr fileid = (uint64_t)roottobn(pmp, 0) * dirsperblk; 315171758Sbde fileid += (uoff_t)dep->de_diroffset / sizeof(struct direntry); 3162893Sdfr } 317166340Srodrigc 318166340Srodrigc if (pmp->pm_flags & MSDOSFS_LARGEFS) 319166340Srodrigc vap->va_fileid = msdosfs_fileno_map(pmp->pm_mountp, fileid); 320166340Srodrigc else 321166340Srodrigc vap->va_fileid = (long)fileid; 322166340Srodrigc 323254627Sken mode = S_IRWXU|S_IRWXG|S_IRWXO; 324118837Strhodes vap->va_mode = mode & 325171758Sbde (ap->a_vp->v_type == VDIR ? pmp->pm_dirmask : pmp->pm_mask); 32633548Sjkh vap->va_uid = pmp->pm_uid; 32733548Sjkh vap->va_gid = pmp->pm_gid; 3282893Sdfr vap->va_nlink = 1; 329183214Skib vap->va_rdev = NODEV; 3302893Sdfr vap->va_size = dep->de_FileSize; 331163647Sphk fattime2timespec(dep->de_MDate, dep->de_MTime, 0, 0, &vap->va_mtime); 332164855Smaxim vap->va_ctime = vap->va_mtime; 33333548Sjkh if (pmp->pm_flags & MSDOSFSMNT_LONGNAME) { 334163647Sphk fattime2timespec(dep->de_ADate, 0, 0, 0, &vap->va_atime); 335163647Sphk fattime2timespec(dep->de_CDate, dep->de_CTime, dep->de_CHun, 336164855Smaxim 0, &vap->va_birthtime); 33733548Sjkh } else { 33833548Sjkh vap->va_atime = vap->va_mtime; 339164855Smaxim vap->va_birthtime.tv_sec = -1; 340164855Smaxim vap->va_birthtime.tv_nsec = 0; 34133548Sjkh } 34233548Sjkh vap->va_flags = 0; 343254627Sken if (dep->de_Attributes & ATTR_ARCHIVE) 344254627Sken vap->va_flags |= UF_ARCHIVE; 345254627Sken if (dep->de_Attributes & ATTR_HIDDEN) 346254627Sken vap->va_flags |= UF_HIDDEN; 347254627Sken if (dep->de_Attributes & ATTR_READONLY) 348254627Sken vap->va_flags |= UF_READONLY; 349254627Sken if (dep->de_Attributes & ATTR_SYSTEM) 350254627Sken vap->va_flags |= UF_SYSTEM; 3512893Sdfr vap->va_gen = 0; 35233548Sjkh vap->va_blocksize = pmp->pm_bpcluster; 35333548Sjkh vap->va_bytes = 35433548Sjkh (dep->de_FileSize + pmp->pm_crbomask) & ~pmp->pm_crbomask; 3552893Sdfr vap->va_type = ap->a_vp->v_type; 3569862Sdfr vap->va_filerev = dep->de_modrev; 35733548Sjkh return (0); 3582893Sdfr} 3592893Sdfr 36011921Sphkstatic int 3612893Sdfrmsdosfs_setattr(ap) 3622893Sdfr struct vop_setattr_args /* { 3632893Sdfr struct vnode *a_vp; 3642893Sdfr struct vattr *a_vap; 3652893Sdfr struct ucred *a_cred; 3662893Sdfr } */ *ap; 3672893Sdfr{ 36811644Sdg struct vnode *vp = ap->a_vp; 3692893Sdfr struct denode *dep = VTODE(ap->a_vp); 37033548Sjkh struct msdosfsmount *pmp = dep->de_pmp; 3712893Sdfr struct vattr *vap = ap->a_vap; 3722893Sdfr struct ucred *cred = ap->a_cred; 373182371Sattilio struct thread *td = curthread; 37411644Sdg int error = 0; 3758876Srgrimes 37633548Sjkh#ifdef MSDOSFS_DEBUG 377182371Sattilio printf("msdosfs_setattr(): vp %p, vap %p, cred %p\n", 378182371Sattilio ap->a_vp, vap, cred); 37933548Sjkh#endif 38033548Sjkh 38111644Sdg /* 38211644Sdg * Check for unsettable attributes. 38311644Sdg */ 38411644Sdg if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) || 38511644Sdg (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) || 38611644Sdg (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) || 38711644Sdg (vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) { 38833548Sjkh#ifdef MSDOSFS_DEBUG 38933548Sjkh printf("msdosfs_setattr(): returning EINVAL\n"); 39033548Sjkh printf(" va_type %d, va_nlink %x, va_fsid %lx, va_fileid %lx\n", 39133548Sjkh vap->va_type, vap->va_nlink, vap->va_fsid, vap->va_fileid); 39233548Sjkh printf(" va_blocksize %lx, va_rdev %x, va_bytes %qx, va_gen %lx\n", 39333548Sjkh vap->va_blocksize, vap->va_rdev, vap->va_bytes, vap->va_gen); 39433548Sjkh printf(" va_uid %x, va_gid %x\n", 39533548Sjkh vap->va_uid, vap->va_gid); 39633548Sjkh#endif 39711644Sdg return (EINVAL); 3982893Sdfr } 399254627Sken 400254627Sken /* 401254627Sken * We don't allow setting attributes on the root directory. 402254627Sken * The special case for the root directory is because before 403254627Sken * FAT32, the root directory didn't have an entry for itself 404254627Sken * (and was otherwise special). With FAT32, the root 405254627Sken * directory is not so special, but still doesn't have an 406254627Sken * entry for itself. 407254627Sken */ 408254627Sken if (vp->v_vflag & VV_ROOT) 409254627Sken return (EINVAL); 410254627Sken 41111644Sdg if (vap->va_flags != VNOVAL) { 41211644Sdg if (vp->v_mount->mnt_flag & MNT_RDONLY) 41311644Sdg return (EROFS); 414164033Srwatson if (cred->cr_uid != pmp->pm_uid) { 415170587Srwatson error = priv_check_cred(cred, PRIV_VFS_ADMIN, 0); 416164033Srwatson if (error) 417164033Srwatson return (error); 418164033Srwatson } 41911644Sdg /* 42011644Sdg * We are very inconsistent about handling unsupported 42136811Sdt * attributes. We ignored the access time and the 42211644Sdg * read and execute bits. We were strict for the other 42311644Sdg * attributes. 42411644Sdg */ 425254627Sken if (vap->va_flags & ~(UF_ARCHIVE | UF_HIDDEN | UF_READONLY | 426254627Sken UF_SYSTEM)) 42736839Speter return EOPNOTSUPP; 428254627Sken if (vap->va_flags & UF_ARCHIVE) 429254627Sken dep->de_Attributes |= ATTR_ARCHIVE; 430254627Sken else 43111644Sdg dep->de_Attributes &= ~ATTR_ARCHIVE; 432254627Sken if (vap->va_flags & UF_HIDDEN) 433254627Sken dep->de_Attributes |= ATTR_HIDDEN; 434254627Sken else 435254627Sken dep->de_Attributes &= ~ATTR_HIDDEN; 436254627Sken /* We don't allow changing the readonly bit on directories. */ 437254627Sken if (vp->v_type != VDIR) { 438254627Sken if (vap->va_flags & UF_READONLY) 439254627Sken dep->de_Attributes |= ATTR_READONLY; 440254627Sken else 441254627Sken dep->de_Attributes &= ~ATTR_READONLY; 442254627Sken } 443254627Sken if (vap->va_flags & UF_SYSTEM) 444254627Sken dep->de_Attributes |= ATTR_SYSTEM; 445254627Sken else 446254627Sken dep->de_Attributes &= ~ATTR_SYSTEM; 44711644Sdg dep->de_flag |= DE_MODIFIED; 44811644Sdg } 4492893Sdfr 45033548Sjkh if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) { 45133548Sjkh uid_t uid; 45233548Sjkh gid_t gid; 453111742Sdes 45411644Sdg if (vp->v_mount->mnt_flag & MNT_RDONLY) 45511644Sdg return (EROFS); 45633548Sjkh uid = vap->va_uid; 45733548Sjkh if (uid == (uid_t)VNOVAL) 45833548Sjkh uid = pmp->pm_uid; 45933548Sjkh gid = vap->va_gid; 46033548Sjkh if (gid == (gid_t)VNOVAL) 46133548Sjkh gid = pmp->pm_gid; 462164033Srwatson if (cred->cr_uid != pmp->pm_uid || uid != pmp->pm_uid || 463164033Srwatson (gid != pmp->pm_gid && !groupmember(gid, cred))) { 464170587Srwatson error = priv_check_cred(cred, PRIV_VFS_CHOWN, 0); 465164033Srwatson if (error) 466164033Srwatson return (error); 467164033Srwatson } 46833548Sjkh if (uid != pmp->pm_uid || gid != pmp->pm_gid) 46936858Sdt return EINVAL; 4705241Sbde } 47133548Sjkh 4722893Sdfr if (vap->va_size != VNOVAL) { 47311644Sdg switch (vp->v_type) { 47411644Sdg case VDIR: 47511644Sdg return (EISDIR); 47611644Sdg case VREG: 477171771Sbde /* 478171771Sbde * Truncation is only supported for regular files, 479171771Sbde * Disallow it if the filesystem is read-only. 480171771Sbde */ 48111644Sdg if (vp->v_mount->mnt_flag & MNT_RDONLY) 48211644Sdg return (EROFS); 48311644Sdg break; 48443305Sdillon default: 485171771Sbde /* 486171771Sbde * According to POSIX, the result is unspecified 487171771Sbde * for file types other than regular files, 488171771Sbde * directories and shared memory objects. We 489171771Sbde * don't support any file types except regular 490171771Sbde * files and directories in this file system, so 491171771Sbde * this (default) case is unreachable and can do 492171771Sbde * anything. Keep falling through to detrunc() 493171771Sbde * for now. 494171771Sbde */ 49543305Sdillon break; 49611644Sdg } 497234605Strasz error = detrunc(dep, vap->va_size, 0, cred); 4983152Sphk if (error) 4992893Sdfr return error; 5002893Sdfr } 50133548Sjkh if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) { 50211644Sdg if (vp->v_mount->mnt_flag & MNT_RDONLY) 50311644Sdg return (EROFS); 504164033Srwatson if (vap->va_vaflags & VA_UTIMES_NULL) { 505182371Sattilio error = VOP_ACCESS(vp, VADMIN, cred, td); 506164033Srwatson if (error) 507182371Sattilio error = VOP_ACCESS(vp, VWRITE, cred, td); 508164033Srwatson } else 509182371Sattilio error = VOP_ACCESS(vp, VADMIN, cred, td); 510254627Sken if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95) == 0 && 511254627Sken vap->va_atime.tv_sec != VNOVAL) { 512254627Sken dep->de_flag &= ~DE_ACCESS; 513254627Sken timespec2fattime(&vap->va_atime, 0, 514254627Sken &dep->de_ADate, NULL, NULL); 515254627Sken } 516254627Sken if (vap->va_mtime.tv_sec != VNOVAL) { 517254627Sken dep->de_flag &= ~DE_UPDATE; 518254627Sken timespec2fattime(&vap->va_mtime, 0, 519254627Sken &dep->de_MDate, &dep->de_MTime, NULL); 520254627Sken } 521254627Sken /* 522254627Sken * We don't set the archive bit when modifying the time of 523254627Sken * a directory to emulate the Windows/DOS behavior. 524254627Sken */ 525254627Sken if (vp->v_type != VDIR) 52633548Sjkh dep->de_Attributes |= ATTR_ARCHIVE; 527254627Sken dep->de_flag |= DE_MODIFIED; 5282893Sdfr } 5292893Sdfr /* 5305241Sbde * DOS files only have the ability to have their writability 5312893Sdfr * attribute set, so we use the owner write bit to set the readonly 5322893Sdfr * attribute. 5332893Sdfr */ 53433548Sjkh if (vap->va_mode != (mode_t)VNOVAL) { 53511644Sdg if (vp->v_mount->mnt_flag & MNT_RDONLY) 53611644Sdg return (EROFS); 537164033Srwatson if (cred->cr_uid != pmp->pm_uid) { 538170587Srwatson error = priv_check_cred(cred, PRIV_VFS_ADMIN, 0); 539164033Srwatson if (error) 540164033Srwatson return (error); 541164033Srwatson } 54233548Sjkh if (vp->v_type != VDIR) { 54333548Sjkh /* We ignore the read and execute bits. */ 54433548Sjkh if (vap->va_mode & VWRITE) 54533548Sjkh dep->de_Attributes &= ~ATTR_READONLY; 54633548Sjkh else 54733548Sjkh dep->de_Attributes |= ATTR_READONLY; 54893883Sbde dep->de_Attributes |= ATTR_ARCHIVE; 54933548Sjkh dep->de_flag |= DE_MODIFIED; 55033548Sjkh } 5512893Sdfr } 552172741Sbde return (deupdat(dep, 0)); 5532893Sdfr} 5542893Sdfr 55511921Sphkstatic int 5562893Sdfrmsdosfs_read(ap) 5572893Sdfr struct vop_read_args /* { 5582893Sdfr struct vnode *a_vp; 5592893Sdfr struct uio *a_uio; 5602893Sdfr int a_ioflag; 5612893Sdfr struct ucred *a_cred; 5622893Sdfr } */ *ap; 5632893Sdfr{ 5642893Sdfr int error = 0; 56533548Sjkh int blsize; 5662893Sdfr int isadir; 567231949Skib ssize_t orig_resid; 56855308Sbp u_int n; 56955308Sbp u_long diff; 57055308Sbp u_long on; 5712893Sdfr daddr_t lbn; 57234002Smsmith daddr_t rablock; 5732893Sdfr int rasize; 57451486Sdillon int seqcount; 5752893Sdfr struct buf *bp; 5762893Sdfr struct vnode *vp = ap->a_vp; 5772893Sdfr struct denode *dep = VTODE(vp); 5782893Sdfr struct msdosfsmount *pmp = dep->de_pmp; 5792893Sdfr struct uio *uio = ap->a_uio; 5802893Sdfr 5812893Sdfr /* 5822893Sdfr * If they didn't ask for any data, then we are done. 5832893Sdfr */ 58449075Sbde orig_resid = uio->uio_resid; 585171774Sbde if (orig_resid == 0) 58633548Sjkh return (0); 5872893Sdfr 588171774Sbde /* 589171774Sbde * The caller is supposed to ensure that 590171774Sbde * uio->uio_offset >= 0 and uio->uio_resid >= 0. 591171774Sbde * We don't need to check for large offsets as in ffs because 592171774Sbde * dep->de_FileSize <= DOS_FILESIZE_MAX < OFF_MAX, so large 593171774Sbde * offsets cannot cause overflow even in theory. 594171774Sbde */ 595171774Sbde 596108357Sdillon seqcount = ap->a_ioflag >> IO_SEQSHIFT; 59751486Sdillon 5982893Sdfr isadir = dep->de_Attributes & ATTR_DIRECTORY; 5992893Sdfr do { 60055189Sbp if (uio->uio_offset >= dep->de_FileSize) 60155189Sbp break; 60233548Sjkh lbn = de_cluster(pmp, uio->uio_offset); 603171522Sbde rablock = lbn + 1; 604171522Sbde blsize = pmp->pm_bpcluster; 605171522Sbde on = uio->uio_offset & pmp->pm_crbomask; 6062893Sdfr /* 6072893Sdfr * If we are operating on a directory file then be sure to 6082893Sdfr * do i/o with the vnode for the filesystem instead of the 6092893Sdfr * vnode for the directory. 6102893Sdfr */ 6112893Sdfr if (isadir) { 61255308Sbp /* convert cluster # to block # */ 61355308Sbp error = pcbmap(dep, lbn, &lbn, 0, &blsize); 61455594Sbp if (error == E2BIG) { 61555594Sbp error = EINVAL; 61655308Sbp break; 61755594Sbp } else if (error) 61855594Sbp break; 61933548Sjkh error = bread(pmp->pm_devvp, lbn, blsize, NOCRED, &bp); 620171522Sbde } else if (de_cn2off(pmp, rablock) >= dep->de_FileSize) { 621171522Sbde error = bread(vp, lbn, blsize, NOCRED, &bp); 622171523Sbde } else if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERR) == 0) { 623171523Sbde error = cluster_read(vp, dep->de_FileSize, lbn, blsize, 624248282Skib NOCRED, on + uio->uio_resid, seqcount, 0, &bp); 625171522Sbde } else if (seqcount > 1) { 626171522Sbde rasize = blsize; 627171522Sbde error = breadn(vp, lbn, 628171522Sbde blsize, &rablock, &rasize, 1, NOCRED, &bp); 6292893Sdfr } else { 630171522Sbde error = bread(vp, lbn, blsize, NOCRED, &bp); 6312893Sdfr } 6322893Sdfr if (error) { 6332893Sdfr brelse(bp); 63449075Sbde break; 6352893Sdfr } 63655308Sbp diff = pmp->pm_bpcluster - on; 63755308Sbp n = diff > uio->uio_resid ? uio->uio_resid : diff; 63855308Sbp diff = dep->de_FileSize - uio->uio_offset; 63955308Sbp if (diff < n) 64055308Sbp n = diff; 64155308Sbp diff = blsize - bp->b_resid; 64255308Sbp if (diff < n) 64355308Sbp n = diff; 6442893Sdfr error = uiomove(bp->b_data + on, (int) n, uio); 6452893Sdfr brelse(bp); 6462893Sdfr } while (error == 0 && uio->uio_resid > 0 && n != 0); 64749075Sbde if (!isadir && (error == 0 || uio->uio_resid != orig_resid) && 64849075Sbde (vp->v_mount->mnt_flag & MNT_NOATIME) == 0) 64941416Sdt dep->de_flag |= DE_ACCESS; 65033548Sjkh return (error); 6512893Sdfr} 6522893Sdfr 6532893Sdfr/* 6542893Sdfr * Write data to a file or directory. 6552893Sdfr */ 65611921Sphkstatic int 6572893Sdfrmsdosfs_write(ap) 6582893Sdfr struct vop_write_args /* { 6592893Sdfr struct vnode *a_vp; 6602893Sdfr struct uio *a_uio; 6612893Sdfr int a_ioflag; 6622893Sdfr struct ucred *a_cred; 6632893Sdfr } */ *ap; 6642893Sdfr{ 6652893Sdfr int n; 6662893Sdfr int croffset; 667231949Skib ssize_t resid; 66833548Sjkh u_long osize; 6692893Sdfr int error = 0; 6702893Sdfr u_long count; 671171523Sbde int seqcount; 6722893Sdfr daddr_t bn, lastcn; 6732893Sdfr struct buf *bp; 6742893Sdfr int ioflag = ap->a_ioflag; 6752893Sdfr struct uio *uio = ap->a_uio; 6762893Sdfr struct vnode *vp = ap->a_vp; 6772893Sdfr struct vnode *thisvp; 6782893Sdfr struct denode *dep = VTODE(vp); 6792893Sdfr struct msdosfsmount *pmp = dep->de_pmp; 6802893Sdfr struct ucred *cred = ap->a_cred; 6818876Srgrimes 6822893Sdfr#ifdef MSDOSFS_DEBUG 68333548Sjkh printf("msdosfs_write(vp %p, uio %p, ioflag %x, cred %p\n", 68433548Sjkh vp, uio, ioflag, cred); 68533548Sjkh printf("msdosfs_write(): diroff %lu, dirclust %lu, startcluster %lu\n", 68633548Sjkh dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster); 6872893Sdfr#endif 6882893Sdfr 6892893Sdfr switch (vp->v_type) { 6902893Sdfr case VREG: 6912893Sdfr if (ioflag & IO_APPEND) 6922893Sdfr uio->uio_offset = dep->de_FileSize; 6932893Sdfr thisvp = vp; 6942893Sdfr break; 6952893Sdfr case VDIR: 69633548Sjkh return EISDIR; 6972893Sdfr default: 6982893Sdfr panic("msdosfs_write(): bad file type"); 6992893Sdfr } 7002893Sdfr 701171774Sbde /* 702171774Sbde * This is needed (unlike in ffs_write()) because we extend the 703171774Sbde * file outside of the loop but we don't want to extend the file 704171774Sbde * for writes of 0 bytes. 705171774Sbde */ 7062893Sdfr if (uio->uio_resid == 0) 70733548Sjkh return (0); 7082893Sdfr 7092893Sdfr /* 710171774Sbde * The caller is supposed to ensure that 711171774Sbde * uio->uio_offset >= 0 and uio->uio_resid >= 0. 712171774Sbde */ 713171774Sbde if ((uoff_t)uio->uio_offset + uio->uio_resid > DOS_FILESIZE_MAX) 714171774Sbde return (EFBIG); 715171774Sbde 716171774Sbde /* 7172893Sdfr * If they've exceeded their filesize limit, tell them about it. 7182893Sdfr */ 719207719Strasz if (vn_rlimit_fsize(vp, uio, uio->uio_td)) 720207662Strasz return (EFBIG); 7212893Sdfr 7222893Sdfr /* 7232893Sdfr * If the offset we are starting the write at is beyond the end of 7242893Sdfr * the file, then they've done a seek. Unix filesystems allow 7252893Sdfr * files with holes in them, DOS doesn't so we must fill the hole 7262893Sdfr * with zeroed blocks. 7272893Sdfr */ 7282893Sdfr if (uio->uio_offset > dep->de_FileSize) { 7292893Sdfr error = deextend(dep, uio->uio_offset, cred); 7302893Sdfr if (error) 73133548Sjkh return (error); 7322893Sdfr } 7332893Sdfr 7342893Sdfr /* 7352893Sdfr * Remember some values in case the write fails. 7362893Sdfr */ 7372893Sdfr resid = uio->uio_resid; 7382893Sdfr osize = dep->de_FileSize; 7392893Sdfr 7402893Sdfr /* 7412893Sdfr * If we write beyond the end of the file, extend it to its ultimate 7422893Sdfr * size ahead of the time to hopefully get a contiguous area. 7432893Sdfr */ 7442893Sdfr if (uio->uio_offset + resid > osize) { 74533548Sjkh count = de_clcount(pmp, uio->uio_offset + resid) - 74633548Sjkh de_clcount(pmp, osize); 74733548Sjkh error = extendfile(dep, count, NULL, NULL, 0); 74833548Sjkh if (error && (error != ENOSPC || (ioflag & IO_UNIT))) 7492893Sdfr goto errexit; 7502893Sdfr lastcn = dep->de_fc[FC_LASTFC].fc_frcn; 7512893Sdfr } else 7522893Sdfr lastcn = de_clcount(pmp, osize) - 1; 7538876Srgrimes 754171523Sbde seqcount = ioflag >> IO_SEQSHIFT; 7552893Sdfr do { 75633548Sjkh if (de_cluster(pmp, uio->uio_offset) > lastcn) { 7572893Sdfr error = ENOSPC; 7582893Sdfr break; 7592893Sdfr } 7608876Srgrimes 76118640Sdyson croffset = uio->uio_offset & pmp->pm_crbomask; 76218640Sdyson n = min(uio->uio_resid, pmp->pm_bpcluster - croffset); 76318640Sdyson if (uio->uio_offset + n > dep->de_FileSize) { 76418640Sdyson dep->de_FileSize = uio->uio_offset + n; 76518640Sdyson /* The object size needs to be set before buffer is allocated */ 76618640Sdyson vnode_pager_setsize(vp, dep->de_FileSize); 76718640Sdyson } 76818640Sdyson 76934002Smsmith bn = de_cluster(pmp, uio->uio_offset); 7702893Sdfr if ((uio->uio_offset & pmp->pm_crbomask) == 0 771111742Sdes && (de_cluster(pmp, uio->uio_offset + uio->uio_resid) 772111742Sdes > de_cluster(pmp, uio->uio_offset) 7732893Sdfr || uio->uio_offset + uio->uio_resid >= dep->de_FileSize)) { 7742893Sdfr /* 7752893Sdfr * If either the whole cluster gets written, 7762893Sdfr * or we write the cluster from its start beyond EOF, 7772893Sdfr * then no need to read data from disk. 7782893Sdfr */ 779111856Sjeff bp = getblk(thisvp, bn, pmp->pm_bpcluster, 0, 0, 0); 780171522Sbde vfs_bio_clrbuf(bp); 7812893Sdfr /* 7822893Sdfr * Do the bmap now, since pcbmap needs buffers 7832893Sdfr * for the fat table. (see msdosfs_strategy) 7842893Sdfr */ 78533548Sjkh if (bp->b_blkno == bp->b_lblkno) { 78692363Smckusick error = pcbmap(dep, bp->b_lblkno, &bn, 0, 0); 78733548Sjkh if (error) 78833548Sjkh bp->b_blkno = -1; 78992363Smckusick else 79092363Smckusick bp->b_blkno = bn; 7912893Sdfr } 79233548Sjkh if (bp->b_blkno == -1) { 79333548Sjkh brelse(bp); 79433548Sjkh if (!error) 79533548Sjkh error = EIO; /* XXX */ 79633548Sjkh break; 79733548Sjkh } 7982893Sdfr } else { 7992893Sdfr /* 8002893Sdfr * The block we need to write into exists, so read it in. 8012893Sdfr */ 8023152Sphk error = bread(thisvp, bn, pmp->pm_bpcluster, cred, &bp); 80333548Sjkh if (error) { 80433548Sjkh brelse(bp); 8052893Sdfr break; 80633548Sjkh } 8072893Sdfr } 8082893Sdfr 8092893Sdfr /* 8102893Sdfr * Should these vnode_pager_* functions be done on dir 8112893Sdfr * files? 8122893Sdfr */ 8132893Sdfr 8142893Sdfr /* 8152893Sdfr * Copy the data from user space into the buf header. 8162893Sdfr */ 8172893Sdfr error = uiomove(bp->b_data + croffset, n, uio); 81855190Sbp if (error) { 81955190Sbp brelse(bp); 82055190Sbp break; 82155190Sbp } 8222893Sdfr 823171523Sbde /* Prepare for clustered writes in some else clauses. */ 824171523Sbde if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERW) == 0) 825171523Sbde bp->b_flags |= B_CLUSTEROK; 826171523Sbde 8272893Sdfr /* 828171522Sbde * If IO_SYNC, then each buffer is written synchronously. 829171523Sbde * Otherwise, if we have a severe page deficiency then 830171523Sbde * write the buffer asynchronously. Otherwise, if on a 831171522Sbde * cluster boundary then write the buffer asynchronously, 832171523Sbde * combining it with contiguous clusters if permitted and 833171523Sbde * possible, since we don't expect more writes into this 834171522Sbde * buffer soon. Otherwise, do a delayed write because we 835171522Sbde * expect more writes into this buffer soon. 8362893Sdfr */ 8372893Sdfr if (ioflag & IO_SYNC) 838171522Sbde (void)bwrite(bp); 839171523Sbde else if (vm_page_count_severe() || buf_dirty_count_severe()) 8402893Sdfr bawrite(bp); 841171523Sbde else if (n + croffset == pmp->pm_bpcluster) { 842171523Sbde if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERW) == 0) 843171523Sbde cluster_write(vp, bp, dep->de_FileSize, 844248282Skib seqcount, 0); 845171523Sbde else 846171523Sbde bawrite(bp); 847171523Sbde } else 8482893Sdfr bdwrite(bp); 8492893Sdfr dep->de_flag |= DE_UPDATE; 8502893Sdfr } while (error == 0 && uio->uio_resid > 0); 8512893Sdfr 8522893Sdfr /* 8532893Sdfr * If the write failed and they want us to, truncate the file back 8542893Sdfr * to the size it was before the write was attempted. 8552893Sdfr */ 8562893Sdfrerrexit: 8572893Sdfr if (error) { 8582893Sdfr if (ioflag & IO_UNIT) { 859234605Strasz detrunc(dep, osize, ioflag & IO_SYNC, NOCRED); 8602893Sdfr uio->uio_offset -= resid - uio->uio_resid; 8612893Sdfr uio->uio_resid = resid; 8622893Sdfr } else { 863234605Strasz detrunc(dep, dep->de_FileSize, ioflag & IO_SYNC, NOCRED); 8642893Sdfr if (uio->uio_resid != resid) 8652893Sdfr error = 0; 8662893Sdfr } 86733548Sjkh } else if (ioflag & IO_SYNC) 86833548Sjkh error = deupdat(dep, 1); 86933548Sjkh return (error); 8702893Sdfr} 8712893Sdfr 8722893Sdfr/* 8732893Sdfr * Flush the blocks of a file to disk. 8742893Sdfr */ 87511921Sphkstatic int 8762893Sdfrmsdosfs_fsync(ap) 8772893Sdfr struct vop_fsync_args /* { 8782893Sdfr struct vnode *a_vp; 8792893Sdfr struct ucred *a_cred; 8802893Sdfr int a_waitfor; 88183366Sjulian struct thread *a_td; 8822893Sdfr } */ *ap; 8832893Sdfr{ 884250193Skib struct vnode *devvp; 885250193Skib int allerror, error; 886171758Sbde 887110584Sjeff vop_stdfsync(ap); 888250193Skib 889250193Skib /* 890250193Skib * If the syncing request comes from fsync(2), sync the entire 891250193Skib * FAT and any other metadata that happens to be on devvp. We 892250193Skib * need this mainly for the FAT. We write the FAT sloppily, and 893250193Skib * syncing it all now is the best we can easily do to get all 894250193Skib * directory entries associated with the file (not just the file) 895250193Skib * fully synced. The other metadata includes critical metadata 896250193Skib * for all directory entries, but only in the MNT_ASYNC case. We 897250193Skib * will soon sync all metadata in the file's directory entry. 898250193Skib * Non-critical metadata for associated directory entries only 899250193Skib * gets synced accidentally, as in most file systems. 900250193Skib */ 901250193Skib if (ap->a_waitfor == MNT_WAIT) { 902250193Skib devvp = VTODE(ap->a_vp)->de_pmp->pm_devvp; 903250193Skib vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 904250193Skib allerror = VOP_FSYNC(devvp, MNT_WAIT, ap->a_td); 905250193Skib VOP_UNLOCK(devvp, 0); 906250193Skib } else 907250193Skib allerror = 0; 908250193Skib 909250193Skib error = deupdat(VTODE(ap->a_vp), ap->a_waitfor == MNT_WAIT); 910250193Skib if (allerror == 0) 911250193Skib allerror = error; 912250193Skib return (allerror); 9132893Sdfr} 9142893Sdfr 91511921Sphkstatic int 9162893Sdfrmsdosfs_remove(ap) 9172893Sdfr struct vop_remove_args /* { 9182893Sdfr struct vnode *a_dvp; 9192893Sdfr struct vnode *a_vp; 9202893Sdfr struct componentname *a_cnp; 9212893Sdfr } */ *ap; 9222893Sdfr{ 9232893Sdfr struct denode *dep = VTODE(ap->a_vp); 9242893Sdfr struct denode *ddep = VTODE(ap->a_dvp); 92535823Smsmith int error; 9262893Sdfr 92733548Sjkh if (ap->a_vp->v_type == VDIR) 92833548Sjkh error = EPERM; 92933548Sjkh else 93033548Sjkh error = removede(ddep, dep); 9312893Sdfr#ifdef MSDOSFS_DEBUG 93233548Sjkh printf("msdosfs_remove(), dep %p, v_usecount %d\n", dep, ap->a_vp->v_usecount); 9332893Sdfr#endif 93433548Sjkh return (error); 9352893Sdfr} 9362893Sdfr 9372893Sdfr/* 93867438Sbp * DOS filesystems don't know what links are. 9392893Sdfr */ 94011921Sphkstatic int 9412893Sdfrmsdosfs_link(ap) 9422893Sdfr struct vop_link_args /* { 9439842Sdg struct vnode *a_tdvp; 9442893Sdfr struct vnode *a_vp; 9452893Sdfr struct componentname *a_cnp; 9462893Sdfr } */ *ap; 9472893Sdfr{ 94835823Smsmith return (EOPNOTSUPP); 9492893Sdfr} 9502893Sdfr 9512893Sdfr/* 9522893Sdfr * Renames on files require moving the denode to a new hash queue since the 9532893Sdfr * denode's location is used to compute which hash queue to put the file 9542893Sdfr * in. Unless it is a rename in place. For example "mv a b". 9558876Srgrimes * 9562893Sdfr * What follows is the basic algorithm: 9578876Srgrimes * 9582893Sdfr * if (file move) { 9592893Sdfr * if (dest file exists) { 9602893Sdfr * remove dest file 9612893Sdfr * } 9622893Sdfr * if (dest and src in same directory) { 9632893Sdfr * rewrite name in existing directory slot 9642893Sdfr * } else { 9652893Sdfr * write new entry in dest directory 9662893Sdfr * update offset and dirclust in denode 9672893Sdfr * move denode to new hash chain 9682893Sdfr * clear old directory entry 9692893Sdfr * } 9702893Sdfr * } else { 9712893Sdfr * directory move 9722893Sdfr * if (dest directory exists) { 9732893Sdfr * if (dest is not empty) { 9742893Sdfr * return ENOTEMPTY 9752893Sdfr * } 9762893Sdfr * remove dest directory 9772893Sdfr * } 9782893Sdfr * if (dest and src in same directory) { 9792893Sdfr * rewrite name in existing entry 9802893Sdfr * } else { 9812893Sdfr * be sure dest is not a child of src directory 9822893Sdfr * write entry in dest directory 9832893Sdfr * update "." and ".." in moved directory 9842893Sdfr * clear old directory entry for moved directory 9852893Sdfr * } 9862893Sdfr * } 9878876Srgrimes * 9882893Sdfr * On entry: 9892893Sdfr * source's parent directory is unlocked 9902893Sdfr * source file or directory is unlocked 9912893Sdfr * destination's parent directory is locked 9922893Sdfr * destination file or directory is locked if it exists 9938876Srgrimes * 9942893Sdfr * On exit: 9952893Sdfr * all denodes should be released 9962893Sdfr */ 99711921Sphkstatic int 9982893Sdfrmsdosfs_rename(ap) 9992893Sdfr struct vop_rename_args /* { 10002893Sdfr struct vnode *a_fdvp; 10012893Sdfr struct vnode *a_fvp; 10022893Sdfr struct componentname *a_fcnp; 10032893Sdfr struct vnode *a_tdvp; 10042893Sdfr struct vnode *a_tvp; 10052893Sdfr struct componentname *a_tcnp; 10062893Sdfr } */ *ap; 10072893Sdfr{ 100833548Sjkh struct vnode *tdvp = ap->a_tdvp; 100933548Sjkh struct vnode *fvp = ap->a_fvp; 101033548Sjkh struct vnode *fdvp = ap->a_fdvp; 101133548Sjkh struct vnode *tvp = ap->a_tvp; 101233548Sjkh struct componentname *tcnp = ap->a_tcnp; 101333548Sjkh struct componentname *fcnp = ap->a_fcnp; 101433548Sjkh struct denode *ip, *xp, *dp, *zp; 1015180252Skib u_char toname[12], oldname[11]; 101633548Sjkh u_long from_diroffset, to_diroffset; 101733548Sjkh u_char to_count; 101833548Sjkh int doingdirectory = 0, newparent = 0; 10192893Sdfr int error; 1020246217Skib u_long cn, pcl; 10212893Sdfr daddr_t bn; 10222893Sdfr struct denode *fddep; /* from file's parent directory */ 10232893Sdfr struct msdosfsmount *pmp; 10242893Sdfr struct direntry *dotdotp; 10252893Sdfr struct buf *bp; 10262893Sdfr 10272893Sdfr fddep = VTODE(ap->a_fdvp); 10282893Sdfr pmp = fddep->de_pmp; 10292893Sdfr 103033548Sjkh pmp = VFSTOMSDOSFS(fdvp->v_mount); 10312893Sdfr 103233548Sjkh#ifdef DIAGNOSTIC 103333548Sjkh if ((tcnp->cn_flags & HASBUF) == 0 || 103433548Sjkh (fcnp->cn_flags & HASBUF) == 0) 103533548Sjkh panic("msdosfs_rename: no name"); 103633548Sjkh#endif 10372893Sdfr /* 103833548Sjkh * Check for cross-device rename. 10392893Sdfr */ 1040171758Sbde if (fvp->v_mount != tdvp->v_mount || 1041171758Sbde (tvp && fvp->v_mount != tvp->v_mount)) { 104233548Sjkh error = EXDEV; 104333548Sjkhabortit: 104433548Sjkh if (tdvp == tvp) 104533548Sjkh vrele(tdvp); 104633548Sjkh else 104733548Sjkh vput(tdvp); 104833548Sjkh if (tvp) 104933548Sjkh vput(tvp); 105033548Sjkh vrele(fdvp); 105133548Sjkh vrele(fvp); 105233548Sjkh return (error); 105333548Sjkh } 10542893Sdfr 10552893Sdfr /* 105633548Sjkh * If source and dest are the same, do nothing. 10572893Sdfr */ 105833548Sjkh if (tvp == fvp) { 105933548Sjkh error = 0; 106033548Sjkh goto abortit; 106133548Sjkh } 10622893Sdfr 1063175202Sattilio error = vn_lock(fvp, LK_EXCLUSIVE); 106433548Sjkh if (error) 106533548Sjkh goto abortit; 106633548Sjkh dp = VTODE(fdvp); 106733548Sjkh ip = VTODE(fvp); 106833548Sjkh 10692893Sdfr /* 10702893Sdfr * Be sure we are not renaming ".", "..", or an alias of ".". This 10712893Sdfr * leads to a crippled directory tree. It's pretty tough to do a 10722893Sdfr * "ls" or "pwd" with the "." directory entry missing, and "cd .." 10732893Sdfr * doesn't work if the ".." entry is missing. 10742893Sdfr */ 107533548Sjkh if (ip->de_Attributes & ATTR_DIRECTORY) { 107633548Sjkh /* 107733548Sjkh * Avoid ".", "..", and aliases of "." for obvious reasons. 107833548Sjkh */ 107933548Sjkh if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') || 108033548Sjkh dp == ip || 108133548Sjkh (fcnp->cn_flags & ISDOTDOT) || 108233548Sjkh (tcnp->cn_flags & ISDOTDOT) || 108333548Sjkh (ip->de_flag & DE_RENAME)) { 1084175294Sattilio VOP_UNLOCK(fvp, 0); 108533548Sjkh error = EINVAL; 108633548Sjkh goto abortit; 10872893Sdfr } 108833548Sjkh ip->de_flag |= DE_RENAME; 108933548Sjkh doingdirectory++; 10902893Sdfr } 10912893Sdfr 10922893Sdfr /* 109333548Sjkh * When the target exists, both the directory 109433548Sjkh * and target vnodes are returned locked. 10952893Sdfr */ 109633548Sjkh dp = VTODE(tdvp); 109733548Sjkh xp = tvp ? VTODE(tvp) : NULL; 109833548Sjkh /* 109933548Sjkh * Remember direntry place to use for destination 110033548Sjkh */ 110133548Sjkh to_diroffset = dp->de_fndoffset; 110233548Sjkh to_count = dp->de_fndcnt; 110333548Sjkh 110433548Sjkh /* 110533548Sjkh * If ".." must be changed (ie the directory gets a new 110633548Sjkh * parent) then the source directory must not be in the 1107204111Suqs * directory hierarchy above the target, as this would 110833548Sjkh * orphan everything below the source directory. Also 110933548Sjkh * the user must have write permission in the source so 111033548Sjkh * as to be able to change "..". We must repeat the call 111133548Sjkh * to namei, as the parent directory is unlocked by the 111233548Sjkh * call to doscheckpath(). 111333548Sjkh */ 111483366Sjulian error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_thread); 1115175294Sattilio VOP_UNLOCK(fvp, 0); 111633548Sjkh if (VTODE(fdvp)->de_StartCluster != VTODE(tdvp)->de_StartCluster) 11172893Sdfr newparent = 1; 111833548Sjkh if (doingdirectory && newparent) { 111933548Sjkh if (error) /* write access check above */ 11202893Sdfr goto bad; 112133548Sjkh if (xp != NULL) 112233548Sjkh vput(tvp); 112333548Sjkh /* 112433548Sjkh * doscheckpath() vput()'s dp, 112533548Sjkh * so we have to do a relookup afterwards 112633548Sjkh */ 112733548Sjkh error = doscheckpath(ip, dp); 11283152Sphk if (error) 112933548Sjkh goto out; 113033548Sjkh if ((tcnp->cn_flags & SAVESTART) == 0) 113133548Sjkh panic("msdosfs_rename: lost to startdir"); 113233548Sjkh error = relookup(tdvp, &tvp, tcnp); 113333548Sjkh if (error) 113433548Sjkh goto out; 113533548Sjkh dp = VTODE(tdvp); 113633548Sjkh xp = tvp ? VTODE(tvp) : NULL; 11372893Sdfr } 11382893Sdfr 113933548Sjkh if (xp != NULL) { 114033548Sjkh /* 114133548Sjkh * Target must be empty if a directory and have no links 114233548Sjkh * to it. Also, ensure source and target are compatible 114333548Sjkh * (both directories, or both not directories). 114433548Sjkh */ 114533548Sjkh if (xp->de_Attributes & ATTR_DIRECTORY) { 114633548Sjkh if (!dosdirempty(xp)) { 11472893Sdfr error = ENOTEMPTY; 11482893Sdfr goto bad; 11492893Sdfr } 115033548Sjkh if (!doingdirectory) { 115133548Sjkh error = ENOTDIR; 11522893Sdfr goto bad; 11532893Sdfr } 115433548Sjkh cache_purge(tdvp); 115533548Sjkh } else if (doingdirectory) { 115633548Sjkh error = EISDIR; 115733548Sjkh goto bad; 11582893Sdfr } 115933548Sjkh error = removede(dp, xp); 11603152Sphk if (error) 11612893Sdfr goto bad; 116233548Sjkh vput(tvp); 116333548Sjkh xp = NULL; 11642893Sdfr } 11652893Sdfr 11662893Sdfr /* 116733548Sjkh * Convert the filename in tcnp into a dos filename. We copy this 116833548Sjkh * into the denode and directory entry for the destination 116933548Sjkh * file/directory. 11702893Sdfr */ 117133548Sjkh error = uniqdosname(VTODE(tdvp), tcnp, toname); 117233548Sjkh if (error) 117333548Sjkh goto abortit; 117433548Sjkh 117533548Sjkh /* 117633548Sjkh * Since from wasn't locked at various places above, 117733548Sjkh * have to do a relookup here. 117833548Sjkh */ 117933548Sjkh fcnp->cn_flags &= ~MODMASK; 118033548Sjkh fcnp->cn_flags |= LOCKPARENT | LOCKLEAF; 118133548Sjkh if ((fcnp->cn_flags & SAVESTART) == 0) 118233548Sjkh panic("msdosfs_rename: lost from startdir"); 118333548Sjkh if (!newparent) 1184175294Sattilio VOP_UNLOCK(tdvp, 0); 118563141Sdwmalone if (relookup(fdvp, &fvp, fcnp) == 0) 118663141Sdwmalone vrele(fdvp); 118733548Sjkh if (fvp == NULL) { 11882893Sdfr /* 118933548Sjkh * From name has disappeared. 11902893Sdfr */ 119133548Sjkh if (doingdirectory) 119233548Sjkh panic("rename: lost dir entry"); 119333548Sjkh if (newparent) 1194175294Sattilio VOP_UNLOCK(tdvp, 0); 119533548Sjkh vrele(tdvp); 1196155160Sjeff vrele(ap->a_fvp); 119733548Sjkh return 0; 119833548Sjkh } 119933548Sjkh xp = VTODE(fvp); 120033548Sjkh zp = VTODE(fdvp); 120133548Sjkh from_diroffset = zp->de_fndoffset; 120233548Sjkh 120333548Sjkh /* 120433548Sjkh * Ensure that the directory entry still exists and has not 120533548Sjkh * changed till now. If the source is a file the entry may 120633548Sjkh * have been unlinked or renamed. In either case there is 120733548Sjkh * no further work to be done. If the source is a directory 120833548Sjkh * then it cannot have been rmdir'ed or renamed; this is 120933548Sjkh * prohibited by the DE_RENAME flag. 121033548Sjkh */ 121133548Sjkh if (xp != ip) { 121233548Sjkh if (doingdirectory) 121333548Sjkh panic("rename: lost dir entry"); 1214175294Sattilio VOP_UNLOCK(fvp, 0); 121533548Sjkh if (newparent) 1216175294Sattilio VOP_UNLOCK(fdvp, 0); 1217155160Sjeff vrele(ap->a_fvp); 121833548Sjkh xp = NULL; 12192893Sdfr } else { 122033548Sjkh vrele(fvp); 122133548Sjkh xp = NULL; 12222893Sdfr 12232893Sdfr /* 122433548Sjkh * First write a new entry in the destination 122533548Sjkh * directory and mark the entry in the source directory 122633548Sjkh * as deleted. Then move the denode to the correct hash 12272893Sdfr * chain for its new location in the filesystem. And, if 12282893Sdfr * we moved a directory, then update its .. entry to point 122933548Sjkh * to the new parent directory. 12302893Sdfr */ 123133548Sjkh bcopy(ip->de_Name, oldname, 11); 123233548Sjkh bcopy(toname, ip->de_Name, 11); /* update denode */ 123333548Sjkh dp->de_fndoffset = to_diroffset; 123433548Sjkh dp->de_fndcnt = to_count; 123533548Sjkh error = createde(ip, dp, (struct denode **)0, tcnp); 12362893Sdfr if (error) { 123733548Sjkh bcopy(oldname, ip->de_Name, 11); 123833548Sjkh if (newparent) 1239175294Sattilio VOP_UNLOCK(fdvp, 0); 1240175294Sattilio VOP_UNLOCK(fvp, 0); 12412893Sdfr goto bad; 12422893Sdfr } 124333548Sjkh ip->de_refcnt++; 124433548Sjkh zp->de_fndoffset = from_diroffset; 124533548Sjkh error = removede(zp, ip); 12463152Sphk if (error) { 1247154730Strhodes /* XXX should downgrade to ro here, fs is corrupt */ 124833548Sjkh if (newparent) 1249175294Sattilio VOP_UNLOCK(fdvp, 0); 1250175294Sattilio VOP_UNLOCK(fvp, 0); 12512893Sdfr goto bad; 12522893Sdfr } 125333548Sjkh if (!doingdirectory) { 125433548Sjkh error = pcbmap(dp, de_cluster(pmp, to_diroffset), 0, 125533548Sjkh &ip->de_dirclust, 0); 125633548Sjkh if (error) { 1257154730Strhodes /* XXX should downgrade to ro here, fs is corrupt */ 125833548Sjkh if (newparent) 1259175294Sattilio VOP_UNLOCK(fdvp, 0); 1260175294Sattilio VOP_UNLOCK(fvp, 0); 126133548Sjkh goto bad; 126233548Sjkh } 126339128Sdt if (ip->de_dirclust == MSDOSFSROOT) 126439128Sdt ip->de_diroffset = to_diroffset; 126539128Sdt else 126633548Sjkh ip->de_diroffset = to_diroffset & pmp->pm_crbomask; 12672893Sdfr } 126833548Sjkh reinsert(ip); 126933548Sjkh if (newparent) 1270175294Sattilio VOP_UNLOCK(fdvp, 0); 12712893Sdfr } 12722893Sdfr 12732893Sdfr /* 12742893Sdfr * If we moved a directory to a new parent directory, then we must 12752893Sdfr * fixup the ".." entry in the moved directory. 12762893Sdfr */ 127733548Sjkh if (doingdirectory && newparent) { 127833548Sjkh cn = ip->de_StartCluster; 12792893Sdfr if (cn == MSDOSFSROOT) { 12802893Sdfr /* this should never happen */ 12817170Sdg panic("msdosfs_rename(): updating .. in root directory?"); 128233548Sjkh } else 12832893Sdfr bn = cntobn(pmp, cn); 12842893Sdfr error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, 12852893Sdfr NOCRED, &bp); 12862893Sdfr if (error) { 1287154730Strhodes /* XXX should downgrade to ro here, fs is corrupt */ 128833548Sjkh brelse(bp); 1289175294Sattilio VOP_UNLOCK(fvp, 0); 12902893Sdfr goto bad; 12912893Sdfr } 129233548Sjkh dotdotp = (struct direntry *)bp->b_data + 1; 1293246217Skib pcl = dp->de_StartCluster; 1294246217Skib if (FAT32(pmp) && pcl == pmp->pm_rootdirblk) 1295246217Skib pcl = MSDOSFSROOT; 1296246217Skib putushort(dotdotp->deStartCluster, pcl); 129733548Sjkh if (FAT32(pmp)) 1298246217Skib putushort(dotdotp->deHighClust, pcl >> 16); 1299231998Skib if (DOINGASYNC(fvp)) 1300172798Sbde bdwrite(bp); 1301172798Sbde else if ((error = bwrite(bp)) != 0) { 1302154730Strhodes /* XXX should downgrade to ro here, fs is corrupt */ 1303175294Sattilio VOP_UNLOCK(fvp, 0); 13042893Sdfr goto bad; 13052893Sdfr } 130633548Sjkh } 130733548Sjkh 1308213543Skib /* 1309213543Skib * The msdosfs lookup is case insensitive. Several aliases may 1310213543Skib * be inserted for a single directory entry. As a consequnce, 1311213543Skib * name cache purge done by lookup for fvp when DELETE op for 1312213543Skib * namei is specified, might be not enough to expunge all 1313213543Skib * namecache entries that were installed for this direntry. 1314213543Skib */ 1315213508Skib cache_purge(fvp); 1316175294Sattilio VOP_UNLOCK(fvp, 0); 131733548Sjkhbad: 131833548Sjkh if (xp) 131933548Sjkh vput(tvp); 132033548Sjkh vput(tdvp); 132133548Sjkhout: 132233548Sjkh ip->de_flag &= ~DE_RENAME; 132333548Sjkh vrele(fdvp); 132433548Sjkh vrele(fvp); 132533548Sjkh return (error); 132633548Sjkh 13272893Sdfr} 13282893Sdfr 132911921Sphkstatic struct { 13302893Sdfr struct direntry dot; 13312893Sdfr struct direntry dotdot; 133233548Sjkh} dosdirtemplate = { 1333203827Skib { ". ", /* the . entry */ 133433548Sjkh ATTR_DIRECTORY, /* file attribute */ 1335111742Sdes 0, /* reserved */ 133633548Sjkh 0, { 0, 0 }, { 0, 0 }, /* create time & date */ 133733548Sjkh { 0, 0 }, /* access date */ 133833548Sjkh { 0, 0 }, /* high bits of start cluster */ 133933548Sjkh { 210, 4 }, { 210, 4 }, /* modify time & date */ 134033548Sjkh { 0, 0 }, /* startcluster */ 1341111742Sdes { 0, 0, 0, 0 } /* filesize */ 134233548Sjkh }, 1343203827Skib { ".. ", /* the .. entry */ 134433548Sjkh ATTR_DIRECTORY, /* file attribute */ 1345111742Sdes 0, /* reserved */ 134633548Sjkh 0, { 0, 0 }, { 0, 0 }, /* create time & date */ 134733548Sjkh { 0, 0 }, /* access date */ 134833548Sjkh { 0, 0 }, /* high bits of start cluster */ 134933548Sjkh { 210, 4 }, { 210, 4 }, /* modify time & date */ 135033548Sjkh { 0, 0 }, /* startcluster */ 135133548Sjkh { 0, 0, 0, 0 } /* filesize */ 135233548Sjkh } 13532893Sdfr}; 13542893Sdfr 135511921Sphkstatic int 13562893Sdfrmsdosfs_mkdir(ap) 13572893Sdfr struct vop_mkdir_args /* { 13582893Sdfr struct vnode *a_dvp; 1359171758Sbde struct vnode **a_vpp; 13602893Sdfr struvt componentname *a_cnp; 13612893Sdfr struct vattr *a_vap; 13622893Sdfr } */ *ap; 13632893Sdfr{ 136433548Sjkh struct componentname *cnp = ap->a_cnp; 136533548Sjkh struct denode *dep; 136633548Sjkh struct denode *pdep = VTODE(ap->a_dvp); 13672893Sdfr struct direntry *denp; 136833548Sjkh struct msdosfsmount *pmp = pdep->de_pmp; 13692893Sdfr struct buf *bp; 137035823Smsmith u_long newcluster, pcl; 137135823Smsmith int bn; 137235823Smsmith int error; 137335823Smsmith struct denode ndirent; 13742893Sdfr struct timespec ts; 13752893Sdfr 13762893Sdfr /* 13772893Sdfr * If this is the root directory and there is no space left we 13782893Sdfr * can't do anything. This is because the root directory can not 13792893Sdfr * change size. 13802893Sdfr */ 138133548Sjkh if (pdep->de_StartCluster == MSDOSFSROOT 138233548Sjkh && pdep->de_fndoffset >= pdep->de_FileSize) { 138333548Sjkh error = ENOSPC; 138433548Sjkh goto bad2; 13852893Sdfr } 13862893Sdfr 13872893Sdfr /* 13882893Sdfr * Allocate a cluster to hold the about to be created directory. 13892893Sdfr */ 13903152Sphk error = clusteralloc(pmp, 0, 1, CLUST_EOFE, &newcluster, NULL); 139133548Sjkh if (error) 139233548Sjkh goto bad2; 13932893Sdfr 139433548Sjkh bzero(&ndirent, sizeof(ndirent)); 139533548Sjkh ndirent.de_pmp = pmp; 139633548Sjkh ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE; 139734901Sphk getnanotime(&ts); 139833548Sjkh DETIMES(&ndirent, &ts, &ts, &ts); 139933548Sjkh 14002893Sdfr /* 14012893Sdfr * Now fill the cluster with the "." and ".." entries. And write 14022893Sdfr * the cluster to disk. This way it is there for the parent 14032893Sdfr * directory to be pointing at if there were a crash. 14042893Sdfr */ 14052893Sdfr bn = cntobn(pmp, newcluster); 14062893Sdfr /* always succeeds */ 1407111856Sjeff bp = getblk(pmp->pm_devvp, bn, pmp->pm_bpcluster, 0, 0, 0); 14082893Sdfr bzero(bp->b_data, pmp->pm_bpcluster); 14092893Sdfr bcopy(&dosdirtemplate, bp->b_data, sizeof dosdirtemplate); 141033548Sjkh denp = (struct direntry *)bp->b_data; 141133548Sjkh putushort(denp[0].deStartCluster, newcluster); 141233548Sjkh putushort(denp[0].deCDate, ndirent.de_CDate); 141333548Sjkh putushort(denp[0].deCTime, ndirent.de_CTime); 141433548Sjkh denp[0].deCHundredth = ndirent.de_CHun; 141533548Sjkh putushort(denp[0].deADate, ndirent.de_ADate); 141633548Sjkh putushort(denp[0].deMDate, ndirent.de_MDate); 141733548Sjkh putushort(denp[0].deMTime, ndirent.de_MTime); 141833548Sjkh pcl = pdep->de_StartCluster; 1419246217Skib /* 1420246217Skib * Although the root directory has a non-magic starting cluster 1421246217Skib * number for FAT32, chkdsk and fsck_msdosfs still require 1422246217Skib * references to it in dotdot entries to be magic. 1423246217Skib */ 142433548Sjkh if (FAT32(pmp) && pcl == pmp->pm_rootdirblk) 1425246217Skib pcl = MSDOSFSROOT; 142633548Sjkh putushort(denp[1].deStartCluster, pcl); 142733548Sjkh putushort(denp[1].deCDate, ndirent.de_CDate); 142833548Sjkh putushort(denp[1].deCTime, ndirent.de_CTime); 142933548Sjkh denp[1].deCHundredth = ndirent.de_CHun; 143033548Sjkh putushort(denp[1].deADate, ndirent.de_ADate); 143133548Sjkh putushort(denp[1].deMDate, ndirent.de_MDate); 143233548Sjkh putushort(denp[1].deMTime, ndirent.de_MTime); 143333548Sjkh if (FAT32(pmp)) { 143433548Sjkh putushort(denp[0].deHighClust, newcluster >> 16); 1435246217Skib putushort(denp[1].deHighClust, pcl >> 16); 14362893Sdfr } 14372893Sdfr 1438231998Skib if (DOINGASYNC(ap->a_dvp)) 1439172798Sbde bdwrite(bp); 1440172798Sbde else if ((error = bwrite(bp)) != 0) 144133548Sjkh goto bad; 144233548Sjkh 14432893Sdfr /* 14442893Sdfr * Now build up a directory entry pointing to the newly allocated 14452893Sdfr * cluster. This will be written to an empty slot in the parent 14462893Sdfr * directory. 14472893Sdfr */ 144833548Sjkh#ifdef DIAGNOSTIC 144933548Sjkh if ((cnp->cn_flags & HASBUF) == 0) 145033548Sjkh panic("msdosfs_mkdir: no name"); 145133548Sjkh#endif 145233548Sjkh error = uniqdosname(pdep, cnp, ndirent.de_Name); 145333548Sjkh if (error) 145433548Sjkh goto bad; 14552893Sdfr 145633548Sjkh ndirent.de_Attributes = ATTR_DIRECTORY; 145741275Sdt ndirent.de_LowerCase = 0; 145833548Sjkh ndirent.de_StartCluster = newcluster; 145933548Sjkh ndirent.de_FileSize = 0; 146033548Sjkh error = createde(&ndirent, pdep, &dep, cnp); 146133548Sjkh if (error) 146233548Sjkh goto bad; 146333548Sjkh *ap->a_vpp = DETOV(dep); 146433548Sjkh return (0); 146533548Sjkh 146633548Sjkhbad: 146733548Sjkh clusterfree(pmp, newcluster, NULL); 146833548Sjkhbad2: 146933548Sjkh return (error); 14702893Sdfr} 14712893Sdfr 147211921Sphkstatic int 14732893Sdfrmsdosfs_rmdir(ap) 14742893Sdfr struct vop_rmdir_args /* { 14752893Sdfr struct vnode *a_dvp; 14762893Sdfr struct vnode *a_vp; 14772893Sdfr struct componentname *a_cnp; 14782893Sdfr } */ *ap; 14792893Sdfr{ 1480111742Sdes struct vnode *vp = ap->a_vp; 1481111742Sdes struct vnode *dvp = ap->a_dvp; 1482111742Sdes struct componentname *cnp = ap->a_cnp; 1483111742Sdes struct denode *ip, *dp; 148433548Sjkh int error; 1485111742Sdes 148633548Sjkh ip = VTODE(vp); 148733548Sjkh dp = VTODE(dvp); 14882893Sdfr 14892893Sdfr /* 149033548Sjkh * Verify the directory is empty (and valid). 149133548Sjkh * (Rmdir ".." won't be valid since 149233548Sjkh * ".." will contain a reference to 149333548Sjkh * the current directory and thus be 149433548Sjkh * non-empty.) 14952893Sdfr */ 149633548Sjkh error = 0; 149733548Sjkh if (!dosdirempty(ip) || ip->de_flag & DE_RENAME) { 14982893Sdfr error = ENOTEMPTY; 14992893Sdfr goto out; 15002893Sdfr } 15012893Sdfr /* 15022893Sdfr * Delete the entry from the directory. For dos filesystems this 15032893Sdfr * gets rid of the directory entry on disk, the in memory copy 15042893Sdfr * still exists but the de_refcnt is <= 0. This prevents it from 15052893Sdfr * being found by deget(). When the vput() on dep is done we give 15062893Sdfr * up access and eventually msdosfs_reclaim() will be called which 15072893Sdfr * will remove it from the denode cache. 15082893Sdfr */ 150933548Sjkh error = removede(dp, ip); 15103152Sphk if (error) 15112893Sdfr goto out; 15122893Sdfr /* 15132893Sdfr * This is where we decrement the link count in the parent 15142893Sdfr * directory. Since dos filesystems don't do this we just purge 151535823Smsmith * the name cache. 15162893Sdfr */ 151733548Sjkh cache_purge(dvp); 15182893Sdfr /* 15192893Sdfr * Truncate the directory that is being deleted. 15202893Sdfr */ 1521234605Strasz error = detrunc(ip, (u_long)0, IO_SYNC, cnp->cn_cred); 152233548Sjkh cache_purge(vp); 152335823Smsmith 152433548Sjkhout: 152533548Sjkh return (error); 15262893Sdfr} 15272893Sdfr 15282893Sdfr/* 15292893Sdfr * DOS filesystems don't know what symlinks are. 15302893Sdfr */ 153111921Sphkstatic int 15322893Sdfrmsdosfs_symlink(ap) 15332893Sdfr struct vop_symlink_args /* { 15342893Sdfr struct vnode *a_dvp; 15352893Sdfr struct vnode **a_vpp; 15362893Sdfr struct componentname *a_cnp; 15372893Sdfr struct vattr *a_vap; 15382893Sdfr char *a_target; 15392893Sdfr } */ *ap; 15402893Sdfr{ 154133548Sjkh return (EOPNOTSUPP); 15422893Sdfr} 15432893Sdfr 154411921Sphkstatic int 15452893Sdfrmsdosfs_readdir(ap) 15462893Sdfr struct vop_readdir_args /* { 15472893Sdfr struct vnode *a_vp; 15482893Sdfr struct uio *a_uio; 15492893Sdfr struct ucred *a_cred; 15502893Sdfr int *a_eofflag; 155124787Sbde int *a_ncookies; 155224787Sbde u_long **a_cookies; 15532893Sdfr } */ *ap; 15542893Sdfr{ 1555172027Sbde struct mbnambuf nb; 15562893Sdfr int error = 0; 15572893Sdfr int diff; 15582893Sdfr long n; 155933548Sjkh int blsize; 15602893Sdfr long on; 15612893Sdfr u_long cn; 1562131523Stjr uint64_t fileno; 156333548Sjkh u_long dirsperblk; 15642893Sdfr long bias = 0; 156533548Sjkh daddr_t bn, lbn; 15662893Sdfr struct buf *bp; 15672893Sdfr struct denode *dep = VTODE(ap->a_vp); 15682893Sdfr struct msdosfsmount *pmp = dep->de_pmp; 15692893Sdfr struct direntry *dentp; 157033548Sjkh struct dirent dirbuf; 15712893Sdfr struct uio *uio = ap->a_uio; 157233548Sjkh u_long *cookies = NULL; 15733167Sdfr int ncookies = 0; 157433548Sjkh off_t offset, off; 157533548Sjkh int chksum = -1; 15762893Sdfr 15772893Sdfr#ifdef MSDOSFS_DEBUG 157833548Sjkh printf("msdosfs_readdir(): vp %p, uio %p, cred %p, eofflagp %p\n", 157933548Sjkh ap->a_vp, uio, ap->a_cred, ap->a_eofflag); 15802893Sdfr#endif 15812893Sdfr 15822893Sdfr /* 15832893Sdfr * msdosfs_readdir() won't operate properly on regular files since 1584218909Sbrucec * it does i/o only with the filesystem vnode, and hence can 15852893Sdfr * retrieve the wrong block from the buffer cache for a plain file. 15862893Sdfr * So, fail attempts to readdir() on a plain file. 15872893Sdfr */ 15882893Sdfr if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) 158933548Sjkh return (ENOTDIR); 15902893Sdfr 15912893Sdfr /* 159233548Sjkh * To be safe, initialize dirbuf 159333548Sjkh */ 159433548Sjkh bzero(dirbuf.d_name, sizeof(dirbuf.d_name)); 159533548Sjkh 159633548Sjkh /* 15972893Sdfr * If the user buffer is smaller than the size of one dos directory 15982893Sdfr * entry or the file offset is not a multiple of the size of a 15992893Sdfr * directory entry, then we fail the read. 16002893Sdfr */ 160133848Smsmith off = offset = uio->uio_offset; 160233848Smsmith if (uio->uio_resid < sizeof(struct direntry) || 160333548Sjkh (offset & (sizeof(struct direntry) - 1))) 160433548Sjkh return (EINVAL); 16052893Sdfr 160633548Sjkh if (ap->a_ncookies) { 160733548Sjkh ncookies = uio->uio_resid / 16; 1608184205Sdes cookies = malloc(ncookies * sizeof(u_long), M_TEMP, 1609111119Simp M_WAITOK); 161033548Sjkh *ap->a_cookies = cookies; 161133548Sjkh *ap->a_ncookies = ncookies; 161233548Sjkh } 161333548Sjkh 161433548Sjkh dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry); 161533548Sjkh 16162893Sdfr /* 16172893Sdfr * If they are reading from the root directory then, we simulate 16182893Sdfr * the . and .. entries since these don't exist in the root 16192893Sdfr * directory. We also set the offset bias to make up for having to 16202893Sdfr * simulate these entries. By this I mean that at file offset 64 we 16212893Sdfr * read the first entry in the root directory that lives on disk. 16222893Sdfr */ 162333548Sjkh if (dep->de_StartCluster == MSDOSFSROOT 162433548Sjkh || (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk)) { 162533548Sjkh#if 0 162633548Sjkh printf("msdosfs_readdir(): going after . or .. in root dir, offset %d\n", 162733548Sjkh offset); 162833548Sjkh#endif 16292893Sdfr bias = 2 * sizeof(struct direntry); 163033548Sjkh if (offset < bias) { 163133548Sjkh for (n = (int)offset / sizeof(struct direntry); 163233548Sjkh n < 2; n++) { 163333548Sjkh if (FAT32(pmp)) 1634131523Stjr fileno = (uint64_t)cntobn(pmp, 163533548Sjkh pmp->pm_rootdirblk) 163633548Sjkh * dirsperblk; 163733548Sjkh else 1638131523Stjr fileno = 1; 1639166340Srodrigc if (pmp->pm_flags & MSDOSFS_LARGEFS) { 1640166340Srodrigc dirbuf.d_fileno = 1641166340Srodrigc msdosfs_fileno_map(pmp->pm_mountp, 1642166340Srodrigc fileno); 1643166340Srodrigc } else { 1644166340Srodrigc 1645166340Srodrigc dirbuf.d_fileno = (uint32_t)fileno; 1646166340Srodrigc } 164733548Sjkh dirbuf.d_type = DT_DIR; 164833548Sjkh switch (n) { 164933548Sjkh case 0: 165033548Sjkh dirbuf.d_namlen = 1; 165133548Sjkh strcpy(dirbuf.d_name, "."); 165233548Sjkh break; 165333548Sjkh case 1: 165433548Sjkh dirbuf.d_namlen = 2; 165533548Sjkh strcpy(dirbuf.d_name, ".."); 165633548Sjkh break; 165733548Sjkh } 165833548Sjkh dirbuf.d_reclen = GENERIC_DIRSIZ(&dirbuf); 165933548Sjkh if (uio->uio_resid < dirbuf.d_reclen) 166033548Sjkh goto out; 1661111741Sdes error = uiomove(&dirbuf, dirbuf.d_reclen, uio); 166233548Sjkh if (error) 166333548Sjkh goto out; 166433848Smsmith offset += sizeof(struct direntry); 166533848Smsmith off = offset; 166633548Sjkh if (cookies) { 166733548Sjkh *cookies++ = offset; 166833548Sjkh if (--ncookies <= 0) 166933548Sjkh goto out; 167033548Sjkh } 16712893Sdfr } 16722893Sdfr } 16732893Sdfr } 167433548Sjkh 1675172027Sbde mbnambuf_init(&nb); 167633548Sjkh off = offset; 167733548Sjkh while (uio->uio_resid > 0) { 167833548Sjkh lbn = de_cluster(pmp, offset - bias); 167933548Sjkh on = (offset - bias) & pmp->pm_crbomask; 168033548Sjkh n = min(pmp->pm_bpcluster - on, uio->uio_resid); 168133548Sjkh diff = dep->de_FileSize - (offset - bias); 168212265Sbde if (diff <= 0) 168312265Sbde break; 168433548Sjkh n = min(n, diff); 168533548Sjkh error = pcbmap(dep, lbn, &bn, &cn, &blsize); 16862893Sdfr if (error) 16872893Sdfr break; 168833548Sjkh error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp); 16892893Sdfr if (error) { 16902893Sdfr brelse(bp); 169133548Sjkh return (error); 16922893Sdfr } 169333548Sjkh n = min(n, blsize - bp->b_resid); 1694117200Strhodes if (n == 0) { 1695117200Strhodes brelse(bp); 1696117200Strhodes return (EIO); 1697117200Strhodes } 16982893Sdfr 16992893Sdfr /* 170033548Sjkh * Convert from dos directory entries to fs-independent 170133548Sjkh * directory entries. 17022893Sdfr */ 170333548Sjkh for (dentp = (struct direntry *)(bp->b_data + on); 170433548Sjkh (char *)dentp < bp->b_data + on + n; 170533548Sjkh dentp++, offset += sizeof(struct direntry)) { 170633548Sjkh#if 0 170733548Sjkh printf("rd: dentp %08x prev %08x crnt %08x deName %02x attr %02x\n", 170833548Sjkh dentp, prev, crnt, dentp->deName[0], dentp->deAttributes); 170933548Sjkh#endif 17102893Sdfr /* 171133548Sjkh * If this is an unused entry, we can stop. 17122893Sdfr */ 171333548Sjkh if (dentp->deName[0] == SLOT_EMPTY) { 171433548Sjkh brelse(bp); 171533548Sjkh goto out; 171633548Sjkh } 17172893Sdfr /* 171833548Sjkh * Skip deleted entries. 17192893Sdfr */ 172033548Sjkh if (dentp->deName[0] == SLOT_DELETED) { 172133548Sjkh chksum = -1; 1722172027Sbde mbnambuf_init(&nb); 172333548Sjkh continue; 17242893Sdfr } 17258876Srgrimes 172633548Sjkh /* 172733548Sjkh * Handle Win95 long directory entries 172833548Sjkh */ 172933548Sjkh if (dentp->deAttributes == ATTR_WIN95) { 173033548Sjkh if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) 173133548Sjkh continue; 1732172027Sbde chksum = win2unixfn(&nb, 1733172027Sbde (struct winentry *)dentp, chksum, pmp); 173433548Sjkh continue; 173533548Sjkh } 17362893Sdfr 17372893Sdfr /* 173833548Sjkh * Skip volume labels 17392893Sdfr */ 174033548Sjkh if (dentp->deAttributes & ATTR_VOLUME) { 174133548Sjkh chksum = -1; 1742172027Sbde mbnambuf_init(&nb); 174333548Sjkh continue; 17442893Sdfr } 174533548Sjkh /* 174633548Sjkh * This computation of d_fileno must match 174733548Sjkh * the computation of va_fileid in 174833548Sjkh * msdosfs_getattr. 174933548Sjkh */ 175033548Sjkh if (dentp->deAttributes & ATTR_DIRECTORY) { 175133548Sjkh fileno = getushort(dentp->deStartCluster); 175233548Sjkh if (FAT32(pmp)) 175333548Sjkh fileno |= getushort(dentp->deHighClust) << 16; 175433548Sjkh /* if this is the root directory */ 175533548Sjkh if (fileno == MSDOSFSROOT) 175633548Sjkh if (FAT32(pmp)) 1757131523Stjr fileno = (uint64_t)cntobn(pmp, 175833548Sjkh pmp->pm_rootdirblk) 175933548Sjkh * dirsperblk; 176033548Sjkh else 176133548Sjkh fileno = 1; 176233548Sjkh else 1763131523Stjr fileno = (uint64_t)cntobn(pmp, fileno) * 1764131523Stjr dirsperblk; 176533548Sjkh dirbuf.d_type = DT_DIR; 176633548Sjkh } else { 1767171758Sbde fileno = (uoff_t)offset / 1768171758Sbde sizeof(struct direntry); 176933548Sjkh dirbuf.d_type = DT_REG; 177033548Sjkh } 1771166340Srodrigc if (pmp->pm_flags & MSDOSFS_LARGEFS) { 1772166340Srodrigc dirbuf.d_fileno = 1773166340Srodrigc msdosfs_fileno_map(pmp->pm_mountp, fileno); 1774166340Srodrigc } else 1775166340Srodrigc dirbuf.d_fileno = (uint32_t)fileno; 1776166340Srodrigc 1777203827Skib if (chksum != winChksum(dentp->deName)) { 177833548Sjkh dirbuf.d_namlen = dos2unixfn(dentp->deName, 177933548Sjkh (u_char *)dirbuf.d_name, 178041275Sdt dentp->deLowerCase | 178141275Sdt ((pmp->pm_flags & MSDOSFSMNT_SHORTNAME) ? 178241275Sdt (LCASE_BASE | LCASE_EXT) : 0), 1783120492Sfjoe pmp); 1784172027Sbde mbnambuf_init(&nb); 1785120492Sfjoe } else 1786172027Sbde mbnambuf_flush(&nb, &dirbuf); 178733548Sjkh chksum = -1; 178833548Sjkh dirbuf.d_reclen = GENERIC_DIRSIZ(&dirbuf); 178933548Sjkh if (uio->uio_resid < dirbuf.d_reclen) { 179033548Sjkh brelse(bp); 179133548Sjkh goto out; 179233548Sjkh } 1793111741Sdes error = uiomove(&dirbuf, dirbuf.d_reclen, uio); 179433548Sjkh if (error) { 179533548Sjkh brelse(bp); 179633548Sjkh goto out; 179733548Sjkh } 179833548Sjkh if (cookies) { 179933848Smsmith *cookies++ = offset + sizeof(struct direntry); 180033548Sjkh if (--ncookies <= 0) { 180133548Sjkh brelse(bp); 180233548Sjkh goto out; 180333548Sjkh } 180433548Sjkh } 180533848Smsmith off = offset + sizeof(struct direntry); 18062893Sdfr } 18072893Sdfr brelse(bp); 18082893Sdfr } 180933548Sjkhout: 181033548Sjkh /* Subtract unused cookies */ 181133548Sjkh if (ap->a_ncookies) 181233548Sjkh *ap->a_ncookies -= ncookies; 181333548Sjkh 181433848Smsmith uio->uio_offset = off; 18152893Sdfr 18162893Sdfr /* 18173935Spst * Set the eofflag (NFS uses it) 18182893Sdfr */ 181946568Speter if (ap->a_eofflag) { 182033548Sjkh if (dep->de_FileSize - (offset - bias) <= 0) 18213167Sdfr *ap->a_eofflag = 1; 18223167Sdfr else 18233167Sdfr *ap->a_eofflag = 0; 182446568Speter } 182533548Sjkh return (error); 18262893Sdfr} 18272893Sdfr 1828171522Sbde/*- 1829171522Sbde * a_vp - pointer to the file's vnode 1830171522Sbde * a_bn - logical block number within the file (cluster number for us) 1831171522Sbde * a_bop - where to return the bufobj of the special file containing the fs 1832171522Sbde * a_bnp - where to return the "physical" block number corresponding to a_bn 1833171522Sbde * (relative to the special file; units are blocks of size DEV_BSIZE) 1834171522Sbde * a_runp - where to return the "run past" a_bn. This is the count of logical 1835171522Sbde * blocks whose physical blocks (together with a_bn's physical block) 1836171522Sbde * are contiguous. 1837171522Sbde * a_runb - where to return the "run before" a_bn. 18382893Sdfr */ 183911921Sphkstatic int 18402893Sdfrmsdosfs_bmap(ap) 18412893Sdfr struct vop_bmap_args /* { 18422893Sdfr struct vnode *a_vp; 184396572Sphk daddr_t a_bn; 1844137726Sphk struct bufobj **a_bop; 184596572Sphk daddr_t *a_bnp; 18462893Sdfr int *a_runp; 184710551Sdyson int *a_runb; 18482893Sdfr } */ *ap; 18492893Sdfr{ 1850171522Sbde struct denode *dep; 1851171523Sbde struct mount *mp; 1852171522Sbde struct msdosfsmount *pmp; 1853171522Sbde struct vnode *vp; 1854171523Sbde daddr_t runbn; 1855171522Sbde u_long cn; 1856171523Sbde int bnpercn, error, maxio, maxrun, run; 18572893Sdfr 1858171522Sbde vp = ap->a_vp; 1859171522Sbde dep = VTODE(vp); 1860171522Sbde pmp = dep->de_pmp; 1861137726Sphk if (ap->a_bop != NULL) 1862171522Sbde *ap->a_bop = &pmp->pm_devvp->v_bufobj; 18632893Sdfr if (ap->a_bnp == NULL) 186433548Sjkh return (0); 1865171522Sbde if (ap->a_runp != NULL) 18662893Sdfr *ap->a_runp = 0; 1867171522Sbde if (ap->a_runb != NULL) 186810551Sdyson *ap->a_runb = 0; 1869171522Sbde cn = ap->a_bn; 1870171522Sbde if (cn != ap->a_bn) 1871171522Sbde return (EFBIG); 1872171522Sbde error = pcbmap(dep, cn, ap->a_bnp, NULL, NULL); 1873171523Sbde if (error != 0 || (ap->a_runp == NULL && ap->a_runb == NULL)) 1874171523Sbde return (error); 1875171523Sbde 1876171523Sbde mp = vp->v_mount; 1877171523Sbde maxio = mp->mnt_iosize_max / mp->mnt_stat.f_iosize; 1878171523Sbde bnpercn = de_cn2bn(pmp, 1); 1879171523Sbde if (ap->a_runp != NULL) { 1880171523Sbde maxrun = ulmin(maxio - 1, pmp->pm_maxcluster - cn); 1881171523Sbde for (run = 1; run <= maxrun; run++) { 1882171523Sbde if (pcbmap(dep, cn + run, &runbn, NULL, NULL) != 0 || 1883171523Sbde runbn != *ap->a_bnp + run * bnpercn) 1884171523Sbde break; 1885171523Sbde } 1886171523Sbde *ap->a_runp = run - 1; 1887171523Sbde } 1888171523Sbde if (ap->a_runb != NULL) { 1889171523Sbde maxrun = ulmin(maxio - 1, cn); 1890171523Sbde for (run = 1; run < maxrun; run++) { 1891171523Sbde if (pcbmap(dep, cn - run, &runbn, NULL, NULL) != 0 || 1892171523Sbde runbn != *ap->a_bnp - run * bnpercn) 1893171523Sbde break; 1894171523Sbde } 1895171523Sbde *ap->a_runb = run - 1; 1896171523Sbde } 1897171523Sbde return (0); 18982893Sdfr} 18992893Sdfr 190011921Sphkstatic int 19012893Sdfrmsdosfs_strategy(ap) 19022893Sdfr struct vop_strategy_args /* { 190337384Sjulian struct vnode *a_vp; 19042893Sdfr struct buf *a_bp; 19052893Sdfr } */ *ap; 19062893Sdfr{ 19072893Sdfr struct buf *bp = ap->a_bp; 1908136991Sphk struct denode *dep = VTODE(ap->a_vp); 1909137036Sphk struct bufobj *bo; 19102893Sdfr int error = 0; 191192363Smckusick daddr_t blkno; 19122893Sdfr 19132893Sdfr /* 19142893Sdfr * If we don't already know the filesystem relative block number 19152893Sdfr * then get it using pcbmap(). If pcbmap() returns the block 19162893Sdfr * number as -1 then we've got a hole in the file. DOS filesystems 19172893Sdfr * don't allow files with holes, so we shouldn't ever see this. 19182893Sdfr */ 19192893Sdfr if (bp->b_blkno == bp->b_lblkno) { 192092363Smckusick error = pcbmap(dep, bp->b_lblkno, &blkno, 0, 0); 192192363Smckusick bp->b_blkno = blkno; 192233548Sjkh if (error) { 192333548Sjkh bp->b_error = error; 192458934Sphk bp->b_ioflags |= BIO_ERROR; 192559249Sphk bufdone(bp); 1926186194Strasz return (0); 192733548Sjkh } 192833548Sjkh if ((long)bp->b_blkno == -1) 192933548Sjkh vfs_bio_clrbuf(bp); 19302893Sdfr } 19312893Sdfr if (bp->b_blkno == -1) { 193259249Sphk bufdone(bp); 193333548Sjkh return (0); 19342893Sdfr } 19352893Sdfr /* 19362893Sdfr * Read/write the block from/to the disk that contains the desired 19372893Sdfr * file block. 19382893Sdfr */ 1939121205Sphk bp->b_iooffset = dbtob(bp->b_blkno); 1940137036Sphk bo = dep->de_pmp->pm_bo; 1941140051Sphk BO_STRATEGY(bo, bp); 194233548Sjkh return (0); 19432893Sdfr} 19442893Sdfr 194511921Sphkstatic int 19462893Sdfrmsdosfs_print(ap) 19472893Sdfr struct vop_print_args /* { 19482893Sdfr struct vnode *vp; 19492893Sdfr } */ *ap; 19502893Sdfr{ 19512893Sdfr struct denode *dep = VTODE(ap->a_vp); 19522893Sdfr 1953111841Snjl printf("\tstartcluster %lu, dircluster %lu, diroffset %lu, ", 1954103559Snjl dep->de_StartCluster, dep->de_dirclust, dep->de_diroffset); 1955189120Sjhb printf("on dev %s\n", devtoname(dep->de_pmp->pm_dev)); 195633548Sjkh return (0); 19572893Sdfr} 19582893Sdfr 195911921Sphkstatic int 19602893Sdfrmsdosfs_pathconf(ap) 19612893Sdfr struct vop_pathconf_args /* { 19622893Sdfr struct vnode *a_vp; 19632893Sdfr int a_name; 19642893Sdfr int *a_retval; 19652893Sdfr } */ *ap; 19662893Sdfr{ 196733548Sjkh struct msdosfsmount *pmp = VTODE(ap->a_vp)->de_pmp; 196833548Sjkh 19692893Sdfr switch (ap->a_name) { 19702893Sdfr case _PC_LINK_MAX: 19712893Sdfr *ap->a_retval = 1; 197233548Sjkh return (0); 19732893Sdfr case _PC_NAME_MAX: 197433548Sjkh *ap->a_retval = pmp->pm_flags & MSDOSFSMNT_LONGNAME ? WIN_MAXLEN : 12; 197533548Sjkh return (0); 19762893Sdfr case _PC_PATH_MAX: 197733548Sjkh *ap->a_retval = PATH_MAX; 197833548Sjkh return (0); 19792893Sdfr case _PC_CHOWN_RESTRICTED: 19802893Sdfr *ap->a_retval = 1; 198133548Sjkh return (0); 19822893Sdfr case _PC_NO_TRUNC: 19832893Sdfr *ap->a_retval = 0; 198433548Sjkh return (0); 19852893Sdfr default: 198633548Sjkh return (EINVAL); 19872893Sdfr } 198833548Sjkh /* NOTREACHED */ 19892893Sdfr} 19902893Sdfr 1991123873Strhodesstatic int 1992166774Spjdmsdosfs_vptofh(ap) 1993166774Spjd struct vop_vptofh_args /* { 1994166774Spjd struct vnode *a_vp; 1995166774Spjd struct fid *a_fhp; 1996166774Spjd } */ *ap; 1997166774Spjd{ 1998166774Spjd struct denode *dep; 1999166774Spjd struct defid *defhp; 2000166774Spjd 2001166774Spjd dep = VTODE(ap->a_vp); 2002166774Spjd defhp = (struct defid *)ap->a_fhp; 2003166774Spjd defhp->defid_len = sizeof(struct defid); 2004166774Spjd defhp->defid_dirclust = dep->de_dirclust; 2005166774Spjd defhp->defid_dirofs = dep->de_diroffset; 2006166774Spjd /* defhp->defid_gen = dep->de_gen; */ 2007166774Spjd return (0); 2008166774Spjd} 2009166774Spjd 20102893Sdfr/* Global vfs data structures for msdosfs */ 2011138290Sphkstruct vop_vector msdosfs_vnodeops = { 2012138290Sphk .vop_default = &default_vnodeops, 2013140196Sphk 2014138290Sphk .vop_access = msdosfs_access, 2015138290Sphk .vop_bmap = msdosfs_bmap, 2016138290Sphk .vop_cachedlookup = msdosfs_lookup, 2017140965Speadar .vop_open = msdosfs_open, 2018138290Sphk .vop_close = msdosfs_close, 2019138290Sphk .vop_create = msdosfs_create, 2020138290Sphk .vop_fsync = msdosfs_fsync, 2021138290Sphk .vop_getattr = msdosfs_getattr, 2022138290Sphk .vop_inactive = msdosfs_inactive, 2023138290Sphk .vop_link = msdosfs_link, 2024138290Sphk .vop_lookup = vfs_cache_lookup, 2025138290Sphk .vop_mkdir = msdosfs_mkdir, 2026138290Sphk .vop_mknod = msdosfs_mknod, 2027138290Sphk .vop_pathconf = msdosfs_pathconf, 2028138290Sphk .vop_print = msdosfs_print, 2029138290Sphk .vop_read = msdosfs_read, 2030138290Sphk .vop_readdir = msdosfs_readdir, 2031138290Sphk .vop_reclaim = msdosfs_reclaim, 2032138290Sphk .vop_remove = msdosfs_remove, 2033138290Sphk .vop_rename = msdosfs_rename, 2034138290Sphk .vop_rmdir = msdosfs_rmdir, 2035138290Sphk .vop_setattr = msdosfs_setattr, 2036138290Sphk .vop_strategy = msdosfs_strategy, 2037138290Sphk .vop_symlink = msdosfs_symlink, 2038138290Sphk .vop_write = msdosfs_write, 2039166774Spjd .vop_vptofh = msdosfs_vptofh, 20402893Sdfr}; 2041