int msdosfs_getattr(void *v) { struct vop_getattr_args *ap = v; struct denode *dep = VTODE(ap->a_vp); struct msdosfsmount *pmp = dep->de_pmp; struct vattr *vap = ap->a_vap; struct timespec ts; uint32_t fileid; getnanotime(&ts); DETIMES(dep, &ts, &ts, &ts); vap->va_fsid = dep->de_dev; /* * The following computation of the fileid must be the same as * that used in msdosfs_readdir() to compute d_fileno. If not, * pwd doesn't work. * * We now use the starting cluster number as the fileid/fileno. * This works for both files and directories (including the root * directory, on FAT32). Even on FAT32, this will at most be a * 28-bit number, as the high 4 bits of FAT32 cluster numbers * are reserved. * * However, we do need to do something for 0-length files, which * will not have a starting cluster number. * * These files cannot be directories, since (except for /, which * is special-cased anyway) directories contain entries for . and * .., so must have non-zero length. * * In this case, we just create a non-cryptographic hash of the * original fileid calculation, and set the top bit. * * This algorithm has the benefit that all directories, and all * non-zero-length files, will have fileids that are persistent * across mounts and reboots, and that cannot collide (as long * as the filesystem is not corrupt). Zero-length files will * have fileids that are persistent, but that may collide. We * will just have to live with that. */ fileid = dep->de_StartCluster; if (dep->de_Attributes & ATTR_DIRECTORY) { /* Special-case root */ if (dep->de_StartCluster == MSDOSFSROOT) fileid = FAT32(pmp) ? pmp->pm_rootdirblk : 1; } else { if (dep->de_FileSize == 0) { uint32_t dirsperblk; uint64_t fileid64; dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry); fileid64 = (dep->de_dirclust == MSDOSFSROOT) ? roottobn(pmp, 0) : cntobn(pmp, dep->de_dirclust); fileid64 *= dirsperblk; fileid64 += dep->de_diroffset / sizeof(struct direntry); fileid = fileidhash(fileid64); } } vap->va_fileid = fileid; vap->va_mode = (S_IXUSR|S_IXGRP|S_IXOTH) | (S_IRUSR|S_IRGRP|S_IROTH) | ((dep->de_Attributes & ATTR_READONLY) ? 0 : (S_IWUSR|S_IWGRP|S_IWOTH)); vap->va_mode &= dep->de_pmp->pm_mask; if (dep->de_Attributes & ATTR_DIRECTORY) { vap->va_mode |= S_IFDIR; if (pmp->pm_flags & MSDOSFSMNT_ALLOWDIRX) { vap->va_mode |= (vap->va_mode & S_IRUSR) ? S_IXUSR : 0; vap->va_mode |= (vap->va_mode & S_IRGRP) ? S_IXGRP : 0; vap->va_mode |= (vap->va_mode & S_IROTH) ? S_IXOTH : 0; } } vap->va_nlink = 1; vap->va_gid = dep->de_pmp->pm_gid; vap->va_uid = dep->de_pmp->pm_uid; vap->va_rdev = 0; vap->va_size = dep->de_FileSize; dos2unixtime(dep->de_MDate, dep->de_MTime, 0, &vap->va_mtime); if (dep->de_pmp->pm_flags & MSDOSFSMNT_LONGNAME) { dos2unixtime(dep->de_ADate, 0, 0, &vap->va_atime); dos2unixtime(dep->de_CDate, dep->de_CTime, dep->de_CTimeHundredth, &vap->va_ctime); } else { vap->va_atime = vap->va_mtime; vap->va_ctime = vap->va_mtime; } vap->va_flags = 0; if ((dep->de_Attributes & ATTR_ARCHIVE) == 0) vap->va_flags |= SF_ARCHIVED; vap->va_gen = 0; vap->va_blocksize = dep->de_pmp->pm_bpcluster; vap->va_bytes = (dep->de_FileSize + dep->de_pmp->pm_crbomask) & ~(dep->de_pmp->pm_crbomask); vap->va_type = ap->a_vp->v_type; return (0); }
int msdosfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn) { struct denode denode; static struct dosmount *mounts; struct dosmount *mnt; u_long dirsperblk; int fileid; if (!kvm_read_all(kd, (unsigned long)VTODE(vp), &denode, sizeof(denode))) { warnx("can't read denode at %p", (void *)VTODE(vp)); return (1); } /* * Find msdosfsmount structure for the vnode's filesystem. Needed * for some filesystem parameters */ for (mnt = mounts; mnt; mnt = mnt->next) if (mnt->kptr == denode.de_pmp) break; if (!mnt) { if ((mnt = malloc(sizeof(struct dosmount))) == NULL) { warn("malloc()"); return (1); } if (!kvm_read_all(kd, (unsigned long)denode.de_pmp, &mnt->data, sizeof(mnt->data))) { free(mnt); warnx("can't read mount info at %p", (void *)denode.de_pmp); return (1); } mnt->next = mounts; mounts = mnt; mnt->kptr = denode.de_pmp; } vn->vn_fsid = dev2udev(kd, mnt->data.pm_dev); vn->vn_mode = 0555; vn->vn_mode |= denode.de_Attributes & ATTR_READONLY ? 0 : 0222; vn->vn_mode &= mnt->data.pm_mask; /* Distinguish directories and files. No "special" files in FAT. */ vn->vn_mode |= denode.de_Attributes & ATTR_DIRECTORY ? S_IFDIR : S_IFREG; vn->vn_size = denode.de_FileSize; /* * XXX - * Culled from msdosfs_vnops.c. There appears to be a problem * here, in that a directory has the same inode number as the first * file in the directory. stat(2) suffers from this problem also, so * I won't try to fix it here. * * The following computation of the fileid must be the same as that * used in msdosfs_readdir() to compute d_fileno. If not, pwd * doesn't work. */ dirsperblk = mnt->data.pm_BytesPerSec / sizeof(struct direntry); if (denode.de_Attributes & ATTR_DIRECTORY) { fileid = cntobn(&mnt->data, denode.de_StartCluster) * dirsperblk; if (denode.de_StartCluster == MSDOSFSROOT) fileid = 1; } else { fileid = cntobn(&mnt->data, denode.de_dirclust) * dirsperblk; if (denode.de_dirclust == MSDOSFSROOT) fileid = roottobn(&mnt->data, 0) * dirsperblk; fileid += denode.de_diroffset / sizeof(struct direntry); } vn->vn_fileid = fileid; return (0); }
int msdosfs_readdir(void *v) { struct vop_readdir_args *ap = v; int error = 0; int diff; long n; int blsize; long on; long lost; long count; uint32_t dirsperblk; uint32_t cn, lbn; uint32_t fileno; long bias = 0; daddr64_t bn; struct buf *bp; struct denode *dep = VTODE(ap->a_vp); struct msdosfsmount *pmp = dep->de_pmp; struct direntry *dentp; struct dirent dirbuf; struct uio *uio = ap->a_uio; u_long *cookies = NULL; int ncookies = 0; off_t offset, wlast = -1; int chksum = -1; #ifdef MSDOSFS_DEBUG printf("msdosfs_readdir(): vp %08x, uio %08x, cred %08x, eofflagp %08x\n", ap->a_vp, uio, ap->a_cred, ap->a_eofflag); #endif /* * msdosfs_readdir() won't operate properly on regular files since * it does i/o only with the filesystem vnode, and hence can * retrieve the wrong block from the buffer cache for a plain file. * So, fail attempts to readdir() on a plain file. */ if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) return (ENOTDIR); /* * To be safe, initialize dirbuf */ bzero(dirbuf.d_name, sizeof(dirbuf.d_name)); /* * If the user buffer is smaller than the size of one dos directory * entry or the file offset is not a multiple of the size of a * directory entry, then we fail the read. */ count = uio->uio_resid & ~(sizeof(struct direntry) - 1); offset = uio->uio_offset; if (count < sizeof(struct direntry) || (offset & (sizeof(struct direntry) - 1))) return (EINVAL); lost = uio->uio_resid - count; uio->uio_resid = count; if (ap->a_ncookies) { ncookies = uio->uio_resid / sizeof(struct direntry) + 3; cookies = malloc(ncookies * sizeof(u_long), M_TEMP, M_WAITOK); *ap->a_cookies = cookies; *ap->a_ncookies = ncookies; } dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry); /* * If they are reading from the root directory then, we simulate * the . and .. entries since these don't exist in the root * directory. We also set the offset bias to make up for having to * simulate these entries. By this I mean that at file offset 64 we * read the first entry in the root directory that lives on disk. */ if (dep->de_StartCluster == MSDOSFSROOT || (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk)) { #if 0 printf("msdosfs_readdir(): going after . or .. in root dir, offset %d\n", offset); #endif bias = 2 * sizeof(struct direntry); if (offset < bias) { for (n = (int)offset / sizeof(struct direntry); n < 2; n++) { if (FAT32(pmp)) dirbuf.d_fileno = pmp->pm_rootdirblk; else dirbuf.d_fileno = 1; dirbuf.d_type = DT_DIR; switch (n) { case 0: dirbuf.d_namlen = 1; strlcpy(dirbuf.d_name, ".", sizeof dirbuf.d_name); break; case 1: dirbuf.d_namlen = 2; strlcpy(dirbuf.d_name, "..", sizeof dirbuf.d_name); break; } dirbuf.d_reclen = DIRENT_SIZE(&dirbuf); if (uio->uio_resid < dirbuf.d_reclen) goto out; error = uiomove((caddr_t) &dirbuf, dirbuf.d_reclen, uio); if (error) goto out; offset += sizeof(struct direntry); if (cookies) { *cookies++ = offset; if (--ncookies <= 0) goto out; } } } } while (uio->uio_resid > 0) { lbn = de_cluster(pmp, offset - bias); on = (offset - bias) & pmp->pm_crbomask; n = min(pmp->pm_bpcluster - on, uio->uio_resid); diff = dep->de_FileSize - (offset - bias); if (diff <= 0) break; n = min(n, diff); if ((error = pcbmap(dep, lbn, &bn, &cn, &blsize)) != 0) break; error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp); if (error) { brelse(bp); return (error); } n = min(n, blsize - bp->b_resid); /* * Convert from dos directory entries to fs-independent * directory entries. */ for (dentp = (struct direntry *)(bp->b_data + on); (char *)dentp < bp->b_data + on + n; dentp++, offset += sizeof(struct direntry)) { #if 0 printf("rd: dentp %08x prev %08x crnt %08x deName %02x attr %02x\n", dentp, prev, crnt, dentp->deName[0], dentp->deAttributes); #endif /* * If this is an unused entry, we can stop. */ if (dentp->deName[0] == SLOT_EMPTY) { brelse(bp); goto out; } /* * Skip deleted entries. */ if (dentp->deName[0] == SLOT_DELETED) { chksum = -1; wlast = -1; continue; } /* * Handle Win95 long directory entries */ if (dentp->deAttributes == ATTR_WIN95) { struct winentry *wep; if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) continue; wep = (struct winentry *)dentp; chksum = win2unixfn(wep, &dirbuf, chksum); if (wep->weCnt & WIN_LAST) wlast = offset; continue; } /* * Skip volume labels */ if (dentp->deAttributes & ATTR_VOLUME) { chksum = -1; wlast = -1; continue; } /* * This computation of d_fileno must match * the computation of va_fileid in * msdosfs_getattr. */ fileno = getushort(dentp->deStartCluster); if (FAT32(pmp)) fileno |= getushort(dentp->deHighClust) << 16; if (dentp->deAttributes & ATTR_DIRECTORY) { /* Special-case root */ if (fileno == MSDOSFSROOT) { fileno = FAT32(pmp) ? pmp->pm_rootdirblk : 1; } dirbuf.d_fileno = fileno; dirbuf.d_type = DT_DIR; } else { if (getulong(dentp->deFileSize) == 0) { uint64_t fileno64; fileno64 = (cn == MSDOSFSROOT) ? roottobn(pmp, 0) : cntobn(pmp, cn); fileno64 *= dirsperblk; fileno64 += dentp - (struct direntry *)bp->b_data; fileno = fileidhash(fileno64); } dirbuf.d_fileno = fileno; dirbuf.d_type = DT_REG; } if (chksum != winChksum(dentp->deName)) dirbuf.d_namlen = dos2unixfn(dentp->deName, (u_char *)dirbuf.d_name, pmp->pm_flags & MSDOSFSMNT_SHORTNAME); else dirbuf.d_name[dirbuf.d_namlen] = 0; chksum = -1; dirbuf.d_reclen = DIRENT_SIZE(&dirbuf); if (uio->uio_resid < dirbuf.d_reclen) { brelse(bp); /* Remember long-name offset. */ if (wlast != -1) offset = wlast; goto out; } wlast = -1; error = uiomove((caddr_t) &dirbuf, dirbuf.d_reclen, uio); if (error) { brelse(bp); goto out; } if (cookies) { *cookies++ = offset + sizeof(struct direntry); if (--ncookies <= 0) { brelse(bp); goto out; } } } brelse(bp); } out: /* Subtract unused cookies */ if (ap->a_ncookies) *ap->a_ncookies -= ncookies; uio->uio_offset = offset; uio->uio_resid += lost; if (dep->de_FileSize - (offset - bias) <= 0) *ap->a_eofflag = 1; else *ap->a_eofflag = 0; return (error); }