/*
 * dep  - directory entry to copy into the directory
 * ddep - directory to add to
 * depp - return the address of the denode for the created directory entry
 *	  if depp != 0
 * cnp  - componentname needed for Win95 long filenames
 */
int
createde(struct denode *dep, struct denode *ddep, struct denode **depp, struct componentname *cnp)
{
	int error, rberror;
	u_long dirclust, clusoffset;
	u_long fndoffset, havecnt = 0, wcnt = 1, i;
	struct direntry *ndep;
	struct msdosfsmount *pmp = ddep->de_pmp;
	struct buf *bp;
	daddr_t bn;
	int blsize;
#ifdef _KERNEL
	int async = ddep->de_pmp->pm_mountp->mnt_flag & MNT_ASYNC;
#else
#define async 0
#endif

#ifdef MSDOSFS_DEBUG
	printf("createde(dep %p, ddep %p, depp %p, cnp %p)\n",
	    dep, ddep, depp, cnp);
#endif

	/*
	 * If no space left in the directory then allocate another cluster
	 * and chain it onto the end of the file.  There is one exception
	 * to this.  That is, if the root directory has no more space it
	 * can NOT be expanded.  extendfile() checks for and fails attempts
	 * to extend the root directory.  We just return an error in that
	 * case.
	 */
	if (ddep->de_fndoffset >= ddep->de_FileSize) {
		u_long needlen = ddep->de_fndoffset + sizeof(struct direntry)
		    - ddep->de_FileSize;
		dirclust = de_clcount(pmp, needlen);
		if ((error = extendfile(ddep, dirclust, 0, 0, DE_CLEAR)) != 0) {
			(void)detrunc(ddep, ddep->de_FileSize, 0, NOCRED);
			goto err_norollback;
		}

		/*
		 * Update the size of the directory
		 */
		ddep->de_FileSize += de_cn2off(pmp, dirclust);
	}

	/*
	 * We just read in the cluster with space.  Copy the new directory
	 * entry in.  Then write it to disk. NOTE:  DOS directories
	 * do not get smaller as clusters are emptied.
	 */
	error = pcbmap(ddep, de_cluster(pmp, ddep->de_fndoffset),
		       &bn, &dirclust, &blsize);
	if (error)
		goto err_norollback;
	clusoffset = ddep->de_fndoffset;
	if (dirclust != MSDOSFSROOT)
		clusoffset &= pmp->pm_crbomask;
	if ((error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize, NOCRED,
	    B_MODIFY, &bp)) != 0) {
		goto err_norollback;
	}
	ndep = bptoep(pmp, bp, clusoffset);

	DE_EXTERNALIZE(ndep, dep);

	/*
	 * Now write the Win95 long name
	 */
	if (ddep->de_fndcnt > 0) {
		u_int8_t chksum = winChksum(ndep->deName);
		const u_char *un = (const u_char *)cnp->cn_nameptr;
		int unlen = cnp->cn_namelen;
		u_long xhavecnt;

		fndoffset = ddep->de_fndoffset;
		xhavecnt = ddep->de_fndcnt + 1;

		for(; wcnt < xhavecnt; wcnt++) {
			if ((fndoffset & pmp->pm_crbomask) == 0) {
				/* we should never get here if ddep is root
				 * directory */

				if (async)
					(void) bdwrite(bp);
				else if ((error = bwrite(bp)) != 0)
					goto rollback;

				fndoffset -= sizeof(struct direntry);
				error = pcbmap(ddep,
					       de_cluster(pmp, fndoffset),
					       &bn, 0, &blsize);
				if (error)
					goto rollback;

				error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn),
				    blsize, NOCRED, B_MODIFY, &bp);
				if (error) {
					goto rollback;
				}
				ndep = bptoep(pmp, bp,
						fndoffset & pmp->pm_crbomask);
			} else {
				ndep--;
				fndoffset -= sizeof(struct direntry);
			}
			if (!unix2winfn(un, unlen, (struct winentry *)ndep,
						wcnt, chksum))
				break;
		}
	}

	if (async)
		bdwrite(bp);
	else if ((error = bwrite(bp)) != 0)
		goto rollback;

	/*
	 * If they want us to return with the denode gotten.
	 */
	if (depp) {
		u_long diroffset = clusoffset;
		if (dep->de_Attributes & ATTR_DIRECTORY) {
			dirclust = dep->de_StartCluster;
			if (FAT32(pmp) && dirclust == pmp->pm_rootdirblk)
				dirclust = MSDOSFSROOT;
			if (dirclust == MSDOSFSROOT)
				diroffset = MSDOSFSROOT_OFS;
			else
				diroffset = 0;
		}
		error = deget(pmp, dirclust, diroffset, depp);
#ifndef MAKEFS
		if (error == 0)
			VOP_UNLOCK(DETOV(*depp));
#endif
		return error;
	}

	return 0;

    rollback:
	/*
	 * Mark all slots modified so far as deleted. Note that we
	 * can't just call removede(), since directory is not in
	 * consistent state.
	 */
	fndoffset = ddep->de_fndoffset;
	rberror = pcbmap(ddep, de_cluster(pmp, fndoffset),
	       &bn, NULL, &blsize);
	if (rberror)
		goto err_norollback;
	if ((rberror = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize, NOCRED,
	    B_MODIFY, &bp)) != 0) {
		goto err_norollback;
	}
	ndep = bptoep(pmp, bp, clusoffset);

	havecnt = ddep->de_fndcnt + 1;
	for(i = wcnt; i <= havecnt; i++) {
		/* mark entry as deleted */
		ndep->deName[0] = SLOT_DELETED;

		if ((fndoffset & pmp->pm_crbomask) == 0) {
			/* we should never get here if ddep is root
			 * directory */

			if (async)
				bdwrite(bp);
			else if ((rberror = bwrite(bp)) != 0)
				goto err_norollback;

			fndoffset -= sizeof(struct direntry);
			rberror = pcbmap(ddep,
				       de_cluster(pmp, fndoffset),
				       &bn, 0, &blsize);
			if (rberror)
				goto err_norollback;

			rberror = bread(pmp->pm_devvp, de_bn2kb(pmp, bn),
			    blsize, NOCRED, B_MODIFY, &bp);
			if (rberror) {
				goto err_norollback;
			}
			ndep = bptoep(pmp, bp, fndoffset);
		} else {
			ndep--;
			fndoffset -= sizeof(struct direntry);
		}
	}

	/* ignore any further error */
	if (async)
		(void) bdwrite(bp);
	else
		(void) bwrite(bp);

    err_norollback:
	return error;
}
Esempio n. 2
0
/*
 * If deget() succeeds it returns with the gotten denode locked().
 *
 * pmp	     - address of msdosfsmount structure of the filesystem containing
 *	       the denode of interest.  The address of
 *	       the msdosfsmount structure are used.
 * dirclust  - which cluster bp contains, if dirclust is 0 (root directory)
 *	       diroffset is relative to the beginning of the root directory,
 *	       otherwise it is cluster relative.
 * diroffset - offset past begin of cluster of denode we want
 * depp	     - returns the address of the gotten denode.
 */
int
deget(struct msdosfsmount *pmp, u_long dirclust, u_long diroffset,
    struct denode **depp)
{
	int error;
	uint64_t inode;
	struct mount *mntp = pmp->pm_mountp;
	struct direntry *direntptr;
	struct denode *ldep;
	struct vnode *nvp, *xvp;
	struct buf *bp;

#ifdef MSDOSFS_DEBUG
	printf("deget(pmp %p, dirclust %lu, diroffset %lx, depp %p)\n",
	    pmp, dirclust, diroffset, depp);
#endif

	/*
	 * On FAT32 filesystems, root is a (more or less) normal
	 * directory
	 */
	if (FAT32(pmp) && dirclust == MSDOSFSROOT)
		dirclust = pmp->pm_rootdirblk;

	/*
	 * See if the denode is in the denode cache. Use the location of
	 * the directory entry to compute the hash value. For subdir use
	 * address of "." entry. For root dir (if not FAT32) use cluster
	 * MSDOSFSROOT, offset MSDOSFSROOT_OFS
	 *
	 * NOTE: The check for de_refcnt > 0 below insures the denode being
	 * examined does not represent an unlinked but still open file.
	 * These files are not to be accessible even when the directory
	 * entry that represented the file happens to be reused while the
	 * deleted file is still open.
	 */
	inode = (uint64_t)pmp->pm_bpcluster * dirclust + diroffset;

	error = vfs_hash_get(mntp, inode, LK_EXCLUSIVE, curthread, &nvp,
	    de_vncmpf, &inode);
	if (error)
		return (error);
	if (nvp != NULL) {
		*depp = VTODE(nvp);
		KASSERT((*depp)->de_dirclust == dirclust, ("wrong dirclust"));
		KASSERT((*depp)->de_diroffset == diroffset, ("wrong diroffset"));
		return (0);
	}
	ldep = malloc(sizeof(struct denode), M_MSDOSFSNODE, M_WAITOK | M_ZERO);

	/*
	 * Directory entry was not in cache, have to create a vnode and
	 * copy it from the passed disk buffer.
	 */
	/* getnewvnode() does a VREF() on the vnode */
	error = getnewvnode("msdosfs", mntp, &msdosfs_vnodeops, &nvp);
	if (error) {
		*depp = NULL;
		free(ldep, M_MSDOSFSNODE);
		return error;
	}
	nvp->v_data = ldep;
	ldep->de_vnode = nvp;
	ldep->de_flag = 0;
	ldep->de_dirclust = dirclust;
	ldep->de_diroffset = diroffset;
	ldep->de_inode = inode;
	lockmgr(nvp->v_vnlock, LK_EXCLUSIVE, NULL);
	fc_purge(ldep, 0);	/* init the fat cache for this denode */
	error = insmntque(nvp, mntp);
	if (error != 0) {
		free(ldep, M_MSDOSFSNODE);
		*depp = NULL;
		return (error);
	}
	error = vfs_hash_insert(nvp, inode, LK_EXCLUSIVE, curthread, &xvp,
	    de_vncmpf, &inode);
	if (error) {
		*depp = NULL;
		return (error);
	}
	if (xvp != NULL) {
		*depp = xvp->v_data;
		return (0);
	}

	ldep->de_pmp = pmp;
	ldep->de_refcnt = 1;
	/*
	 * Copy the directory entry into the denode area of the vnode.
	 */
	if ((dirclust == MSDOSFSROOT
	     || (FAT32(pmp) && dirclust == pmp->pm_rootdirblk))
	    && diroffset == MSDOSFSROOT_OFS) {
		/*
		 * Directory entry for the root directory. There isn't one,
		 * so we manufacture one. We should probably rummage
		 * through the root directory and find a label entry (if it
		 * exists), and then use the time and date from that entry
		 * as the time and date for the root denode.
		 */
		nvp->v_vflag |= VV_ROOT; /* should be further down XXX */

		ldep->de_Attributes = ATTR_DIRECTORY;
		ldep->de_LowerCase = 0;
		if (FAT32(pmp))
			ldep->de_StartCluster = pmp->pm_rootdirblk;
			/* de_FileSize will be filled in further down */
		else {
			ldep->de_StartCluster = MSDOSFSROOT;
			ldep->de_FileSize = pmp->pm_rootdirsize * DEV_BSIZE;
		}
		/*
		 * fill in time and date so that fattime2timespec() doesn't
		 * spit up when called from msdosfs_getattr() with root
		 * denode
		 */
		ldep->de_CHun = 0;
		ldep->de_CTime = 0x0000;	/* 00:00:00	 */
		ldep->de_CDate = (0 << DD_YEAR_SHIFT) | (1 << DD_MONTH_SHIFT)
		    | (1 << DD_DAY_SHIFT);
		/* Jan 1, 1980	 */
		ldep->de_ADate = ldep->de_CDate;
		ldep->de_MTime = ldep->de_CTime;
		ldep->de_MDate = ldep->de_CDate;
		/* leave the other fields as garbage */
	} else {
		error = readep(pmp, dirclust, diroffset, &bp, &direntptr);
		if (error) {
			/*
			 * The denode does not contain anything useful, so
			 * it would be wrong to leave it on its hash chain.
			 * Arrange for vput() to just forget about it.
			 */
			ldep->de_Name[0] = SLOT_DELETED;

			vput(nvp);
			*depp = NULL;
			return (error);
		}
		(void)DE_INTERNALIZE(ldep, direntptr);
		brelse(bp);
	}

	/*
	 * Fill in a few fields of the vnode and finish filling in the
	 * denode.  Then return the address of the found denode.
	 */
	if (ldep->de_Attributes & ATTR_DIRECTORY) {
		/*
		 * Since DOS directory entries that describe directories
		 * have 0 in the filesize field, we take this opportunity
		 * to find out the length of the directory and plug it into
		 * the denode structure.
		 */
		u_long size;

		/*
		 * XXX it sometimes happens that the "." entry has cluster
		 * number 0 when it shouldn't.  Use the actual cluster number
		 * instead of what is written in directory entry.
		 */
		if (diroffset == 0 && ldep->de_StartCluster != dirclust) {
#ifdef MSDOSFS_DEBUG
			printf("deget(): \".\" entry at clust %lu != %lu\n",
			    dirclust, ldep->de_StartCluster);
#endif
			ldep->de_StartCluster = dirclust;
		}

		nvp->v_type = VDIR;
		if (ldep->de_StartCluster != MSDOSFSROOT) {
			error = pcbmap(ldep, 0xffff, 0, &size, 0);
			if (error == E2BIG) {
				ldep->de_FileSize = de_cn2off(pmp, size);
				error = 0;
			} else {
#ifdef MSDOSFS_DEBUG
				printf("deget(): pcbmap returned %d\n", error);
#endif
			}
		}
	} else
		nvp->v_type = VREG;
	ldep->de_modrev = init_va_filerev();
	*depp = ldep;
	return (0);
}
Esempio n. 3
0
/*
 * If xtaf_deget() succeeds it returns with the gotten denode locked().
 *
 * pmp	     - address of xtafmount structure of the filesystem containing
 *	       the denode of interest.  The address of the xtafmount structure
 *	       is used.
 * dirclust  - which cluster bp contains, if dirclust is 0 (root directory)
 *	       diroffset is relative to the beginning of the root directory,
 *	       otherwise it is cluster relative.  This is the cluster this
	       directory entry came from.
 * diroffset - offset past begin of cluster of denode we want
 * depp	     - returns the address of the gotten denode.
 */
int
xtaf_deget(struct xtafmount *pmp, u_long dirclust, u_long diroffset,
	    struct denode **depp)
{
	int error;
	uint32_t inode;
	struct mount *mntp = pmp->pm_mountp;
	struct direntry *ep;
	struct denode *ldep;
	struct vnode *nvp, *xvp;
	struct buf *bp;

#ifdef XTAF_DEBUG
	printf("xtaf_deget(pmp %p, dirclust %lx, diroffset %lx, depp %p)\n",
	    pmp, dirclust, diroffset, depp);
#endif

	/*
	 * See if the denode is in the denode cache. Use the location of
	 * the directory entry to compute the hash value. For subdir use
	 * address of the first dir entry. For root dir use cluster XTAFROOT,
	 * offset XTAFROOT_OFS
	 */
	inode = (uint32_t)pmp->pm_bpcluster * dirclust + diroffset;

	error = vfs_hash_get(mntp, inode, LK_EXCLUSIVE, curthread, &nvp,
	    de_vncmpf, &inode);
	if (error)
		return (error);
	if (nvp != NULL) {
		*depp = VTODE(nvp);
		KASSERT((*depp)->de_dirclust == dirclust, ("wrong dirclust"));
		KASSERT((*depp)->de_diroffset == diroffset, ("wrong diroffset"));
		return (0);
	}

	/*
	 * Do the malloc before the getnewvnode since doing so afterward
	 * might cause a bogus v_data pointer to get dereferenced
	 * elsewhere if malloc should block.
	 */
	ldep = malloc(sizeof(struct denode), M_XTAFNODE, M_WAITOK | M_ZERO);

	/*
	 * Directory entry was not in cache, have to create a vnode and
	 * copy it from the passed disk buffer.
	 */
	/* getnewvnode() does a VREF() on the vnode */
	error = getnewvnode("xtaf", mntp, &xtaf_vnodeops, &nvp);
	if (error) {
		*depp = NULL;
		free(ldep, M_XTAFNODE);
		return error;
	}
	nvp->v_data = ldep;
	ldep->de_vnode = nvp;
	ldep->de_flag = 0;
	ldep->de_dirclust = dirclust;
	ldep->de_diroffset = diroffset;
	ldep->de_inode = inode;

	lockmgr(nvp->v_vnlock, LK_EXCLUSIVE, NULL);
	xtaf_fc_purge(ldep, 0);	/* init the fat cache for this denode */
	error = insmntque(nvp, mntp);
	if (error != 0) {
		free(ldep, M_XTAFNODE);
		*depp = NULL;
		return (error);
	}
	error = vfs_hash_insert(nvp, inode, LK_EXCLUSIVE, curthread, &xvp,
	    de_vncmpf, &inode);
	if (error) {
		*depp = NULL;
		return (error);
	}
	if (xvp != NULL) {
		*depp = xvp->v_data;
		return (0);
	}

	ldep->de_pmp = pmp;
	ldep->de_refcnt = 1;
#ifdef XTAF_DEBUG
	printf("xtaf_deget(): dirclust=%lx diroffset=%lx\n", dirclust, diroffset);
#endif
	/*
	 * Copy the directory entry into the denode area of the vnode.
	 */
	if (dirclust == XTAFROOT && diroffset == XTAFROOT_OFS) {
		/*
		 * Directory entry for the root directory. There isn't one,
		 * so we manufacture one.
		 */
		nvp->v_vflag |= VV_ROOT;
		FAKEDIR(ldep, pmp->pm_rootdirsize, XTAFROOT);
#ifdef XTAF_DEBUG
		printf("xtaf_deget(): FAKEDIR root\n");
#endif
	} else {
		error = xtaf_readep(pmp, dirclust, diroffset, &bp, &ep);
		if (error) {
			/*
			 * The denode does not contain anything useful, so
			 * it would be wrong to leave it on its hash chain.
			 * Arrange for vput() to just forget about it.
			 */
			ldep->de_Length = LEN_DELETED;

			vput(nvp);
			*depp = NULL;
			return (error);
		}
		DE_INTERNALIZE(ldep, ep);
		brelse(bp);
	}

	/*
	 * Fill in a few fields of the vnode and finish filling in the
	 * denode.  Then return the address of the found denode.
	 */
	if (ldep->de_Attributes & ATTR_DIRECTORY) {
		/*
		 * Since XTAF directory entries that describe directories
		 * have 0 in the filesize field, we take this opportunity
		 * to find out the length of the directory and plug it into
		 * the denode structure.
		 */
		u_long size;

#ifdef XTAF_DEBUG
		/* FIXME something goes wrong here, StartCluster shouldn't
		 * be 0xffffffff ?   Filename also gets blanked here and
		 * SF_IMMUTABLE set
		 */
		printf("ldep->de_StartCluster = %lx dirclust = %lx\n", ldep->de_StartCluster, dirclust);
#endif
		nvp->v_type = VDIR;
		if (ldep->de_StartCluster != XTAFROOT) {
			error = xtaf_pcbmap(ldep, 0xffff, 0, &size, 0);
			if (error == E2BIG) {
				ldep->de_FileSize = de_cn2off(pmp, size);
				error = 0;
			} else
				printf("xtaf_deget(): xtaf_pcbmap returned "
				       "%d\n", error);
		}
	} else
		nvp->v_type = VREG;
	ldep->de_modrev = init_va_filerev();
	*depp = ldep;
	return (0);
}
Esempio n. 4
0
/*
 * dep  - directory entry to copy into the directory
 * ddep - directory to add to
 * depp - return the address of the denode for the created directory entry
 *	  if depp != 0
 * cnp  - componentname needed for Win95 long filenames
 */
int
createde(struct denode *dep, struct denode *ddep, struct denode **depp,
	 struct componentname *cnp)
{
	int error;
	u_long dirclust, diroffset;
	struct direntry *ndep;
	struct msdosfsmount *pmp = ddep->de_pmp;
	struct buf *bp;
	daddr_t bn;
	int blsize;

#ifdef MSDOSFS_DEBUG
	kprintf("createde(dep %p, ddep %p, depp %p, cnp %p)\n",
	    dep, ddep, depp, cnp);
#endif

	/*
	 * If no space left in the directory then allocate another cluster
	 * and chain it onto the end of the file.  There is one exception
	 * to this.  That is, if the root directory has no more space it
	 * can NOT be expanded.  extendfile() checks for and fails attempts
	 * to extend the root directory.  We just return an error in that
	 * case.
	 */
	if (ddep->de_fndoffset >= ddep->de_FileSize) {
		diroffset = ddep->de_fndoffset + sizeof(struct direntry)
		    - ddep->de_FileSize;
		dirclust = de_clcount(pmp, diroffset);
		error = extendfile(ddep, dirclust, 0, 0, DE_CLEAR);
		if (error) {
			detrunc(ddep, ddep->de_FileSize, 0);
			return error;
		}

		/*
		 * Update the size of the directory
		 */
		ddep->de_FileSize += de_cn2off(pmp, dirclust);
	}

	/*
	 * We just read in the cluster with space.  Copy the new directory
	 * entry in.  Then write it to disk. NOTE:  DOS directories
	 * do not get smaller as clusters are emptied.
	 */
	error = pcbmap(ddep, de_cluster(pmp, ddep->de_fndoffset),
		       &bn, &dirclust, &blsize);
	if (error)
		return error;
	diroffset = ddep->de_fndoffset;
	if (dirclust != MSDOSFSROOT)
		diroffset &= pmp->pm_crbomask;
	if ((error = bread(pmp->pm_devvp, de_bntodoff(pmp, bn), blsize, &bp)) != 0) {
		brelse(bp);
		return error;
	}
	ndep = bptoep(pmp, bp, ddep->de_fndoffset);

	DE_EXTERNALIZE(ndep, dep);

	/*
	 * Now write the Win95 long name
	 */
	if (ddep->de_fndcnt > 0) {
		u_int8_t chksum = winChksum(ndep->deName);
		const u_char *un = (const u_char *)cnp->cn_nameptr;
		int unlen = cnp->cn_namelen;
		int cnt = 1;

		while (--ddep->de_fndcnt >= 0) {
			if (!(ddep->de_fndoffset & pmp->pm_crbomask)) {
				if ((error = bwrite(bp)) != 0)
					return error;

				ddep->de_fndoffset -= sizeof(struct direntry);
				error = pcbmap(ddep,
					       de_cluster(pmp,
							  ddep->de_fndoffset),
					       &bn, NULL, &blsize);
				if (error)
					return error;

				error = bread(pmp->pm_devvp, de_bntodoff(pmp, bn), blsize, &bp);
				if (error) {
					brelse(bp);
					return error;
				}
				ndep = bptoep(pmp, bp, ddep->de_fndoffset);
			} else {
				ndep--;
				ddep->de_fndoffset -= sizeof(struct direntry);
			}
			if (!unix2winfn(un, unlen, (struct winentry *)ndep,
					cnt++, chksum,
					pmp))
				break;
		}
	}

	if ((error = bwrite(bp)) != 0)
		return error;

	/*
	 * If they want us to return with the denode gotten.
	 */
	if (depp) {
		if (dep->de_Attributes & ATTR_DIRECTORY) {
			dirclust = dep->de_StartCluster;
			if (FAT32(pmp) && dirclust == pmp->pm_rootdirblk)
				dirclust = MSDOSFSROOT;
			if (dirclust == MSDOSFSROOT)
				diroffset = MSDOSFSROOT_OFS;
			else
				diroffset = 0;
		}
		return deget(pmp, dirclust, diroffset, depp);
	}

	return 0;
}
Esempio n. 5
0
/*
 * If deget() succeeds it returns with the gotten denode locked(). 
 *
 * pmp	     - address of msdosfsmount structure of the filesystem containing
 *	       the denode of interest.  The pm_dev field and the address of
 *	       the msdosfsmount structure are used. 
 * dirclust  - which cluster bp contains, if dirclust is 0 (root directory)
 *	       diroffset is relative to the beginning of the root directory,
 *	       otherwise it is cluster relative. 
 * diroffset - offset past begin of cluster of denode we want 
 * depp	     - returns the address of the gotten denode.
 */
int
deget(struct msdosfsmount *pmp, uint32_t dirclust, uint32_t diroffset,
    struct denode **depp)
{
	int error;
	extern struct vops msdosfs_vops;
	struct direntry *direntptr;
	struct denode *ldep;
	struct vnode *nvp;
	struct buf *bp;
	struct proc *p = curproc; /* XXX */

#ifdef MSDOSFS_DEBUG
	printf("deget(pmp %08x, dirclust %d, diroffset %x, depp %08x)\n",
	    pmp, dirclust, diroffset, depp);
#endif

	/*
	 * On FAT32 filesystems, root is a (more or less) normal
	 * directory
	 */
	if (FAT32(pmp) && dirclust == MSDOSFSROOT)
		dirclust = pmp->pm_rootdirblk;

	/*
	 * See if the denode is in the denode cache. Use the location of
	 * the directory entry to compute the hash value. For subdir use
	 * address of "." entry. For root dir (if not FAT32) use cluster
	 * MSDOSFSROOT, offset MSDOSFSROOT_OFS
	 * 
	 * NOTE: The check for de_refcnt > 0 below insures the denode being
	 * examined does not represent an unlinked but still open file.
	 * These files are not to be accessible even when the directory
	 * entry that represented the file happens to be reused while the
	 * deleted file is still open.
	 */
retry:
	ldep = msdosfs_hashget(pmp->pm_dev, dirclust, diroffset);
	if (ldep) {
		*depp = ldep;
		return (0);
	}

	/*
	 * Directory entry was not in cache, have to create a vnode and
	 * copy it from the passed disk buffer.
	 */
	/* getnewvnode() does a vref() on the vnode */
	error = getnewvnode(VT_MSDOSFS, pmp->pm_mountp, &msdosfs_vops, &nvp);
	if (error) {
		*depp = 0;
		return (error);
	}
	ldep = malloc(sizeof(*ldep), M_MSDOSFSNODE, M_WAITOK | M_ZERO);
	lockinit(&ldep->de_lock, PINOD, "denode", 0, 0);
	nvp->v_data = ldep;
	ldep->de_vnode = nvp;
	ldep->de_flag = 0;
	ldep->de_devvp = 0;
	ldep->de_lockf = 0;
	ldep->de_dev = pmp->pm_dev;
	ldep->de_dirclust = dirclust;
	ldep->de_diroffset = diroffset;
	fc_purge(ldep, 0);	/* init the fat cache for this denode */

	/*
	 * Insert the denode into the hash queue and lock the denode so it
	 * can't be accessed until we've read it in and have done what we
	 * need to it.
	 */
	vn_lock(nvp, LK_EXCLUSIVE | LK_RETRY, p);
	error = msdosfs_hashins(ldep);

	if (error) {
		vput (nvp);
		
		if (error == EEXIST)
			goto retry;

		return (error);
	}

	ldep->de_pmp = pmp;
	ldep->de_devvp = pmp->pm_devvp;
	ldep->de_refcnt = 1;
	/*
	 * Copy the directory entry into the denode area of the vnode.
	 */
	if ((dirclust == MSDOSFSROOT
	     || (FAT32(pmp) && dirclust == pmp->pm_rootdirblk))
	    && diroffset == MSDOSFSROOT_OFS) {
		/*
		 * Directory entry for the root directory. There isn't one,
		 * so we manufacture one. We should probably rummage
		 * through the root directory and find a label entry (if it
		 * exists), and then use the time and date from that entry
		 * as the time and date for the root denode.
		 */
	        nvp->v_flag |= VROOT; /* should be further down         XXX */

		ldep->de_Attributes = ATTR_DIRECTORY;
		if (FAT32(pmp))
		        ldep->de_StartCluster = pmp->pm_rootdirblk;
		        /* de_FileSize will be filled in further down */
		else {
		        ldep->de_StartCluster = MSDOSFSROOT;
		        ldep->de_FileSize = pmp->pm_rootdirsize * pmp->pm_BytesPerSec;
		}
		/*
		 * fill in time and date so that dos2unixtime() doesn't
		 * spit up when called from msdosfs_getattr() with root
		 * denode
		 */
		ldep->de_CTime = 0x0000;	/* 00:00:00	 */
		ldep->de_CTimeHundredth = 0;
		ldep->de_CDate = (0 << DD_YEAR_SHIFT) | (1 << DD_MONTH_SHIFT)
		    | (1 << DD_DAY_SHIFT);
		/* Jan 1, 1980	 */
		ldep->de_ADate = ldep->de_CDate;
		ldep->de_MTime = ldep->de_CTime;
		ldep->de_MDate = ldep->de_CDate;
		/* leave the other fields as garbage */
	} else {
		error = readep(pmp, dirclust, diroffset, &bp, &direntptr);
		if (error)
			return (error);
		DE_INTERNALIZE(ldep, direntptr);
		brelse(bp);
	}

	/*
	 * Fill in a few fields of the vnode and finish filling in the
	 * denode.  Then return the address of the found denode.
	 */
	if (ldep->de_Attributes & ATTR_DIRECTORY) {
		/*
		 * Since DOS directory entries that describe directories
		 * have 0 in the filesize field, we take this opportunity
		 * to find out the length of the directory and plug it into
		 * the denode structure.
		 */
		uint32_t size;

		nvp->v_type = VDIR;
		if (ldep->de_StartCluster != MSDOSFSROOT) {
			error = pcbmap(ldep, 0xffff, 0, &size, 0);
			if (error == E2BIG) {
				ldep->de_FileSize = de_cn2off(pmp, size);
				error = 0;
			} else if (error) {
				printf("deget(): pcbmap returned %d\n", error);
				return (error);
			}
		}
	} else
		nvp->v_type = VREG;
	vref(ldep->de_devvp);
	*depp = ldep;
	return (0);
}
/*
 * If deget() succeeds it returns with the gotten denode locked().
 *
 * pmp	     - address of msdosfsmount structure of the filesystem containing
 *	       the denode of interest.  The pm_dev field and the address of
 *	       the msdosfsmount structure are used.
 * dirclust  - which cluster bp contains, if dirclust is 0 (root directory)
 *	       diroffset is relative to the beginning of the root directory,
 *	       otherwise it is cluster relative.
 * diroffset - offset past begin of cluster of denode we want
 * depp	     - returns the address of the gotten denode.
 */
int
deget(struct msdosfsmount *pmp, u_long dirclust, u_long diroffset,
    struct denode **depp)
	/* pmp:	 so we know the maj/min number */
	/* dirclust:		 cluster this dir entry came from */
	/* diroffset:		 index of entry within the cluster */
	/* depp:		 returns the addr of the gotten denode */
{
	int error;
	struct direntry *direntptr;
	struct denode *ldep;
	struct buf *bp;

#ifdef MSDOSFS_DEBUG
	printf("deget(pmp %p, dirclust %lu, diroffset %lx, depp %p)\n",
	    pmp, dirclust, diroffset, depp);
#endif

	/*
	 * On FAT32 filesystems, root is a (more or less) normal
	 * directory
	 */
	if (FAT32(pmp) && dirclust == MSDOSFSROOT)
		dirclust = pmp->pm_rootdirblk;

	ldep = ecalloc(1, sizeof(*ldep));
	ldep->de_vnode = NULL;
	ldep->de_flag = 0;
	ldep->de_devvp = 0;
	ldep->de_lockf = 0;
	ldep->de_dev = pmp->pm_dev;
	ldep->de_dirclust = dirclust;
	ldep->de_diroffset = diroffset;
	ldep->de_pmp = pmp;
	ldep->de_devvp = pmp->pm_devvp;
	ldep->de_refcnt = 1;
	fc_purge(ldep, 0);
	/*
	 * Copy the directory entry into the denode area of the vnode.
	 */
	if ((dirclust == MSDOSFSROOT
	     || (FAT32(pmp) && dirclust == pmp->pm_rootdirblk))
	    && diroffset == MSDOSFSROOT_OFS) {
		/*
		 * Directory entry for the root directory. There isn't one,
		 * so we manufacture one. We should probably rummage
		 * through the root directory and find a label entry (if it
		 * exists), and then use the time and date from that entry
		 * as the time and date for the root denode.
		 */
		ldep->de_vnode = (struct vnode *)-1;

		ldep->de_Attributes = ATTR_DIRECTORY;
		if (FAT32(pmp))
			ldep->de_StartCluster = pmp->pm_rootdirblk;
			/* de_FileSize will be filled in further down */
		else {
			ldep->de_StartCluster = MSDOSFSROOT;
			ldep->de_FileSize = pmp->pm_rootdirsize * pmp->pm_BytesPerSec;
		}
		/*
		 * fill in time and date so that dos2unixtime() doesn't
		 * spit up when called from msdosfs_getattr() with root
		 * denode
		 */
		ldep->de_CHun = 0;
		ldep->de_CTime = 0x0000;	/* 00:00:00	 */
		ldep->de_CDate = (0 << DD_YEAR_SHIFT) | (1 << DD_MONTH_SHIFT)
		    | (1 << DD_DAY_SHIFT);
		/* Jan 1, 1980	 */
		ldep->de_ADate = ldep->de_CDate;
		ldep->de_MTime = ldep->de_CTime;
		ldep->de_MDate = ldep->de_CDate;
		/* leave the other fields as garbage */
	} else {
		error = readep(pmp, dirclust, diroffset, &bp, &direntptr);
		if (error) {
			ldep->de_devvp = NULL;
			ldep->de_Name[0] = SLOT_DELETED;
			return (error);
		}
		DE_INTERNALIZE(ldep, direntptr);
		brelse(bp, 0);
	}

	/*
	 * Fill in a few fields of the vnode and finish filling in the
	 * denode.  Then return the address of the found denode.
	 */
	if (ldep->de_Attributes & ATTR_DIRECTORY) {
		/*
		 * Since DOS directory entries that describe directories
		 * have 0 in the filesize field, we take this opportunity
		 * to find out the length of the directory and plug it into
		 * the denode structure.
		 */
		u_long size;

		if (ldep->de_StartCluster != MSDOSFSROOT) {
			error = pcbmap(ldep, CLUST_END, 0, &size, 0);
			if (error == E2BIG) {
				ldep->de_FileSize = de_cn2off(pmp, size);
				error = 0;
			} else
				printf("deget(): pcbmap returned %d\n", error);
		}
	}
	*depp = ldep;
	return (0);
}
Esempio n. 7
0
int
msdosfs_read(void *v)
{
	struct vop_read_args *ap = v;
	int error = 0;
	uint32_t diff;
	int blsize;
	int isadir;
	uint32_t n;
	long on;
	daddr64_t lbn, rablock, rablkno;
	struct buf *bp;
	struct vnode *vp = ap->a_vp;
	struct denode *dep = VTODE(vp);
	struct msdosfsmount *pmp = dep->de_pmp;
	struct uio *uio = ap->a_uio;

	/*
	 * If they didn't ask for any data, then we are done.
	 */
	if (uio->uio_resid == 0)
		return (0);
	if (uio->uio_offset < 0)
		return (EINVAL);

	isadir = dep->de_Attributes & ATTR_DIRECTORY;
	do {
		if (uio->uio_offset >= dep->de_FileSize)
			return (0);

		lbn = de_cluster(pmp, uio->uio_offset);
		on = uio->uio_offset & pmp->pm_crbomask;
		n = min((uint32_t) (pmp->pm_bpcluster - on), uio->uio_resid);

		/*
		 * de_FileSize is uint32_t, and we know that uio_offset <
		 * de_FileSize, so uio->uio_offset < 2^32.  Therefore
		 * the cast to uint32_t on the next line is safe.
		 */
		diff = dep->de_FileSize - (uint32_t)uio->uio_offset;
		if (diff < n)
			n = diff;

		/* convert cluster # to block # if a directory */
		if (isadir) {
			error = pcbmap(dep, lbn, &lbn, 0, &blsize);
			if (error)
				return (error);
		}
		/*
		 * If we are operating on a directory file then be sure to
		 * do i/o with the vnode for the filesystem instead of the
		 * vnode for the directory.
		 */
		if (isadir) {
			error = bread(pmp->pm_devvp, lbn, blsize, NOCRED, &bp);
		} else {
			rablock = lbn + 1;
			rablkno = de_cn2bn(pmp, rablock);
			if (dep->de_lastr + 1 == lbn &&
			    de_cn2off(pmp, rablock) < dep->de_FileSize)
				error = breadn(vp, de_cn2bn(pmp, lbn),
				    pmp->pm_bpcluster, &rablkno,
				    &pmp->pm_bpcluster, 1, NOCRED, &bp);
			else
				error = bread(vp, de_cn2bn(pmp, lbn),
				    pmp->pm_bpcluster, NOCRED, &bp);
			dep->de_lastr = lbn;
		}
		n = min(n, pmp->pm_bpcluster - bp->b_resid);
		if (error) {
			brelse(bp);
			return (error);
		}
		error = uiomove(bp->b_data + on, (int) n, uio);
		brelse(bp);
	} while (error == 0 && uio->uio_resid > 0 && n != 0);
	if (!isadir && !(vp->v_mount->mnt_flag & MNT_NOATIME))
		dep->de_flag |= DE_ACCESS;
	return (error);
}