示例#1
0
/*
 * Clear an Address/Length List so that it holds no pairs.
 */
void
alenlist_clear(alenlist_t alenlist)
{
	alenlist_chunk_t chunk, freechunk;

	/*
	 * If this List is not FIXED_SIZE, free all the
	 * extra chunks.
	 */
	if (!(alenlist->al_flags & AL_FIXED_SIZE)) {
		/* First, free any extension alenlist chunks */
		chunk = alenlist->al_chunk.alc_next;
		while (chunk) {
			freechunk = chunk;
			chunk = chunk->alc_next;
			snia_kmem_zone_free(alenlist_chunk_zone, freechunk);
			DECR_COUNT(&alenlist_chunk_count);
		}
		alenlist->al_actual_size = ALEN_CHUNK_SZ;
		alenlist->al_chunk.alc_next = NULL;
	}

	alenlist->al_logical_size = 0;
	alenlist->al_last_chunk = &alenlist->al_chunk;
	do_cursor_init(alenlist, &alenlist->al_cursor);
}
示例#2
0
/*
 * Destroy an Address/Length List.
 */
void 
alenlist_destroy(alenlist_t alenlist)
{
	if (alenlist == NULL)
		return;

	/* 
	 * Turn off FIXED_SIZE so this List can be 
	 * automatically shrunk.
	 */
	alenlist->al_flags &= ~AL_FIXED_SIZE;

	/* Free extension chunks first */
	if (alenlist->al_chunk.alc_next)
		alenlist_clear(alenlist);

	/* Now, free the alenlist itself */
	snia_kmem_zone_free(alenlist_zone, alenlist);
	DECR_COUNT(&alenlist_count);
}
示例#3
0
/*
 * xdirtrunc is called to remove all directory entries under this directory.
 * The files themselves are removed elsewhere.
 */
void
xdirtrunc(struct xmemnode *dir)
{
	register struct xdirent *xdp;
	size_t namelen;
	timestruc_t now;

	ASSERT(RW_WRITE_HELD(&dir->xn_rwlock));
	ASSERT(dir->xn_type == VDIR);

	for (xdp = dir->xn_dir; xdp; xdp = dir->xn_dir) {
		ASSERT(xdp->xd_next != xdp);
		ASSERT(xdp->xd_prev != xdp);
		ASSERT(xdp->xd_xmemnode);
		ASSERT(xdp->xd_xmemnode->xn_nlink > 0);

		dir->xn_dir = xdp->xd_next;
		namelen = strlen(xdp->xd_name) + 1;

		DECR_COUNT(&xdp->xd_xmemnode->xn_nlink,
		    &xdp->xd_xmemnode->xn_tlock);

		xmemfs_hash_out(xdp);

		xmem_memfree(xdp, sizeof (struct xdirent) + namelen);
		dir->xn_size -= (sizeof (struct xdirent) + namelen);
		dir->xn_dirents--;
	}

	gethrestime(&now);
	dir->xn_mtime = now;
	dir->xn_ctime = now;

	ASSERT(dir->xn_dir == NULL);
	ASSERT(dir->xn_size == 0);
	ASSERT(dir->xn_dirents == 0);
}
示例#4
0
/*
 * Free an Address/Length List cursor.
 */
void
alenlist_cursor_destroy(alenlist_cursor_t cursorp)
{
	DECR_COUNT(&alenlist_cursor_count);
	snia_kmem_zone_free(alenlist_cursor_zone, cursorp);
}
示例#5
0
static int
xdirrename(
	struct xmemnode *fromparent,	/* parent directory of source */
	struct xmemnode *fromxp,	/* source xmemnode */
	struct xmemnode *toparent,	/* parent directory of target */
	char *nm,			/* entry we are trying to change */
	struct xmemnode *to,		/* target xmemnode */
	struct xdirent *where,		/* target xmemnode directory entry */
	struct cred *cred)		/* credentials */
{
	int error = 0;
	int doingdirectory;
	timestruc_t now;

#if defined(lint)
	nm = nm;
#endif
	ASSERT(RW_WRITE_HELD(&toparent->xn_rwlock));

	rw_enter(&fromxp->xn_rwlock, RW_READER);
	rw_enter(&to->xn_rwlock, RW_READER);

	/*
	 * Check that everything is on the same filesystem.
	 */
	if (to->xn_vnode->v_vfsp != toparent->xn_vnode->v_vfsp ||
	    to->xn_vnode->v_vfsp != fromxp->xn_vnode->v_vfsp) {
		error = EXDEV;
		goto out;
	}

	/*
	 * Short circuit rename of something to itself.
	 */
	if (fromxp == to) {
		error = ESAME;		/* special KLUDGE error code */
		goto out;
	}

	/*
	 * Must have write permission to rewrite target entry.
	 */
	if (error = xmem_xaccess(fromparent, VWRITE, cred))
		goto out;

	/*
	 * If the parent directory is "sticky", then the user must own
	 * either the parent directory or the destination of the rename,
	 * or else must have permission to write the destination.
	 * Otherwise the destination may not be changed (except by the
	 * privileged users).  This implements append-only directories.
	 */
	if (error = xmem_sticky_remove_access(toparent, to, cred))
		goto out;

	/*
	 * Ensure source and target are compatible (both directories
	 * or both not directories).  If target is a directory it must
	 * be empty and have no links to it; in addition it must not
	 * be a mount point, and both the source and target must be
	 * writable.
	 */
	doingdirectory = (fromxp->xn_type == VDIR);
	if (to->xn_type == VDIR) {
		if (!doingdirectory) {
			error = EISDIR;
			goto out;
		}
		/*
		 * vn_vfswlock will prevent mounts from using the directory
		 * until we are done.
		 */
		if (vn_vfswlock(XNTOV(to))) {
			error = EBUSY;
			goto out;
		}
		if (vn_mountedvfs(XNTOV(to)) != NULL) {
			vn_vfsunlock(XNTOV(to));
			error = EBUSY;
			goto out;
		}

		mutex_enter(&to->xn_tlock);
		if (to->xn_dirents > 2 || to->xn_nlink > 2) {
			mutex_exit(&to->xn_tlock);
			vn_vfsunlock(XNTOV(to));
			error = EEXIST; /* SIGH should be ENOTEMPTY */
			/*
			 * Update atime because checking xn_dirents is
			 * logically equivalent to reading the directory
			 */
			gethrestime(&to->xn_atime);
			goto out;
		}
		mutex_exit(&to->xn_tlock);
	} else if (doingdirectory) {
		error = ENOTDIR;
		goto out;
	}

	where->xd_xmemnode = fromxp;
	gethrestime(&now);
	toparent->xn_mtime = now;
	toparent->xn_ctime = now;

	/*
	 * Upgrade to write lock on "to" (i.e., the target xmemnode).
	 */
	rw_exit(&to->xn_rwlock);
	rw_enter(&to->xn_rwlock, RW_WRITER);

	/*
	 * Decrement the link count of the target xmemnode.
	 */
	DECR_COUNT(&to->xn_nlink, &to->xn_tlock);
	to->xn_ctime = now;

	if (doingdirectory) {
		/*
		 * The entry for "to" no longer exists so release the vfslock.
		 */
		vn_vfsunlock(XNTOV(to));

		/*
		 * Decrement the target link count and delete all entires.
		 */
		xdirtrunc(to);
		ASSERT(to->xn_nlink == 0);

		/*
		 * Renaming a directory with the parent different
		 * requires that ".." be rewritten.  The window is
		 * still there for ".." to be inconsistent, but this
		 * is unavoidable, and a lot shorter than when it was
		 * done in a user process.
		 */
		if (fromparent != toparent)
			xdirfixdotdot(fromxp, fromparent, toparent);
	}
out:
	rw_exit(&to->xn_rwlock);
	rw_exit(&fromxp->xn_rwlock);
	return (error);
}
示例#6
0
/*
 * Delete entry xp of name "nm" from dir.
 * Free dir entry space and decrement link count on xmemnode(s).
 *
 * Return 0 on success.
 */
int
xdirdelete(
	struct xmemnode *dir,
	struct xmemnode *xp,
	char *nm,
	enum dr_op op,
	struct cred *cred)
{
	register struct xdirent *tpdp;
	int error;
	size_t namelen;
	struct xmemnode *xptmp;
	timestruc_t now;

	ASSERT(RW_WRITE_HELD(&dir->xn_rwlock));
	ASSERT(RW_WRITE_HELD(&xp->xn_rwlock));
	ASSERT(dir->xn_type == VDIR);

	ASSERT(nm[0] != '\0');

	/*
	 * return error when removing . and ..
	 */
	if (nm[0] == '.') {
		if (nm[1] == '\0')
			return (EINVAL);
		if (nm[1] == '.' && nm[2] == '\0')
			return (EEXIST); /* thus in ufs */
	}

	if (error = xmem_xaccess(dir, VEXEC|VWRITE, cred))
		return (error);

	/*
	 * If the parent directory is "sticky", then the user must
	 * own the parent directory or the file in it, or else must
	 * have permission to write the file.  Otherwise it may not
	 * be deleted (except by privileged users).  Same as ufs_dirremove.
	 */
	if (error = xmem_sticky_remove_access(dir, xp, cred))
		return (error);

	if (dir->xn_dir == NULL)
		return (ENOENT);

	tpdp = xmemfs_hash_lookup(nm, dir, 0, &xptmp);
	if (tpdp == NULL) {
		/*
		 * If it is gone, some other thread got here first!
		 * Return error ENOENT.
		 */
		return (ENOENT);
	}

	/*
	 * If the xmemnode in the xdirent changed, we were probably
	 * the victim of a concurrent rename operation.  The original
	 * is gone, so return that status (same as UFS).
	 */
	if (xp != xptmp)
		return (ENOENT);

	xmemfs_hash_out(tpdp);

	/*
	 * Take tpdp out of the directory list.
	 */
	ASSERT(tpdp->xd_next != tpdp);
	ASSERT(tpdp->xd_prev != tpdp);
	if (tpdp->xd_prev) {
		tpdp->xd_prev->xd_next = tpdp->xd_next;
	}
	if (tpdp->xd_next) {
		tpdp->xd_next->xd_prev = tpdp->xd_prev;
	}

	/*
	 * If the roving slot pointer happens to match tpdp,
	 * point it at the previous dirent.
	 */
	if (dir->xn_dir->xd_prev == tpdp) {
		dir->xn_dir->xd_prev = tpdp->xd_prev;
	}
	ASSERT(tpdp->xd_next != tpdp);
	ASSERT(tpdp->xd_prev != tpdp);

	/*
	 * tpdp points to the correct directory entry
	 */
	namelen = strlen(tpdp->xd_name) + 1;

	xmem_memfree(tpdp, sizeof (struct xdirent) + namelen);
	dir->xn_size -= (sizeof (struct xdirent) + namelen);
	dir->xn_dirents--;

	gethrestime(&now);
	dir->xn_mtime = now;
	dir->xn_ctime = now;
	xp->xn_ctime = now;

	ASSERT(xp->xn_nlink > 0);
	DECR_COUNT(&xp->xn_nlink, &xp->xn_tlock);
	if (op == DR_RMDIR && xp->xn_type == VDIR) {
		xdirtrunc(xp);
		ASSERT(xp->xn_nlink == 0);
	}
	return (0);
}
示例#7
0
/*
 * Enter a directory entry for 'name' and 'xp' into directory 'dir'
 *
 * Returns 0 on success.
 */
int
xdirenter(
	struct xmount	*xm,
	struct xmemnode	*dir,		/* target directory to make entry in */
	char		*name,		/* name of entry */
	enum de_op	op,		/* entry operation */
	struct xmemnode	*fromparent,	/* source directory if rename */
	struct xmemnode	*xp,		/* source xmemnode, if link/rename */
	struct vattr	*va,
	struct xmemnode	**xpp,		/* return xmemnode, if create/mkdir */
	struct cred	*cred)
{
	struct xdirent *xdp;
	struct xmemnode *found = NULL;
	int error = 0;
	char *s;

	/*
	 * xn_rwlock is held to serialize direnter and dirdeletes
	 */
	ASSERT(RW_WRITE_HELD(&dir->xn_rwlock));
	ASSERT(dir->xn_type == VDIR);

	/*
	 * Don't allow '/' characters in pathname component
	 * (thus in ufs_direnter()).
	 */
	for (s = name; *s; s++)
		if (*s == '/')
			return (EACCES);

	ASSERT(name[0] != '\0');

	/*
	 * For link and rename lock the source entry and check the link count
	 * to see if it has been removed while it was unlocked.
	 */
	if (op == DE_LINK || op == DE_RENAME) {
		mutex_enter(&xp->xn_tlock);
		if (xp->xn_nlink == 0) {
			mutex_exit(&xp->xn_tlock);
			return (ENOENT);
		}

		if (xp->xn_nlink == MAXLINK) {
			mutex_exit(&xp->xn_tlock);
			return (EMLINK);
		}
		xp->xn_nlink++;
		mutex_exit(&xp->xn_tlock);
		gethrestime(&xp->xn_ctime);
	}

	/*
	 * This might be a "dangling detached directory".
	 * it could have been removed, but a reference
	 * to it kept in u_cwd.  don't bother searching
	 * it, and with any luck the user will get tired
	 * of dealing with us and cd to some absolute
	 * pathway.  *sigh*, thus in ufs, too.
	 */
	if (dir->xn_nlink == 0) {
		error = ENOENT;
		goto out;
	}

	/*
	 * If this is a rename of a directory and the parent is
	 * different (".." must be changed), then the source
	 * directory must not be in the directory hierarchy
	 * above the target, as this would orphan everything
	 * below the source directory.
	 */
	if (op == DE_RENAME) {
		if (xp == dir) {
			error = EINVAL;
			goto out;
		}
		if (xp->xn_type == VDIR) {
			if ((fromparent != dir) &&
			    (error = xdircheckpath(xp, dir, cred))) {
				goto out;
			}
		}
	}

	/*
	 * Search for the entry.  Return "found" if it exists.
	 */
	xdp = xmemfs_hash_lookup(name, dir, 1, &found);

	if (xdp) {
		ASSERT(found);
		switch (op) {
		case DE_CREATE:
		case DE_MKDIR:
			if (xpp) {
				*xpp = found;
				error = EEXIST;
			} else {
				xmemnode_rele(found);
			}
			break;

		case DE_RENAME:
			error = xdirrename(fromparent, xp,
			    dir, name, found, xdp, cred);
			xmemnode_rele(found);
			break;

		case DE_LINK:
			/*
			 * Can't link to an existing file.
			 */
			error = EEXIST;
			xmemnode_rele(found);
			break;
		}
	} else {

		/*
		 * The entry does not exist. Check write permission in
		 * directory to see if entry can be created.
		 */
		if (error = xmem_xaccess(dir, VWRITE, cred))
			goto out;
		if (op == DE_CREATE || op == DE_MKDIR) {
			/*
			 * Make new xmemnode and directory entry as required.
			 */
			error = xdirmakexnode(dir, xm, va, op, &xp, cred);
			if (error)
				goto out;
		}
		if (error = xdiraddentry(dir, xp, name, op, fromparent)) {
			if (op == DE_CREATE || op == DE_MKDIR) {
				/*
				 * Unmake the inode we just made.
				 */
				rw_enter(&xp->xn_rwlock, RW_WRITER);
				if ((xp->xn_type) == VDIR) {
					ASSERT(xdp == NULL);
					/*
					 * cleanup allocs made by xdirinit()
					 */
					xdirtrunc(xp);
				}
				mutex_enter(&xp->xn_tlock);
				xp->xn_nlink = 0;
				mutex_exit(&xp->xn_tlock);
				gethrestime(&xp->xn_ctime);
				rw_exit(&xp->xn_rwlock);
				xmemnode_rele(xp);
				xp = NULL;
			}
		} else if (xpp) {
			*xpp = xp;
		} else if (op == DE_CREATE || op == DE_MKDIR) {
			xmemnode_rele(xp);
		}
	}
out:
	if (error && (op == DE_LINK || op == DE_RENAME)) {
		/*
		 * Undo bumped link count.
		 */
		DECR_COUNT(&xp->xn_nlink, &xp->xn_tlock);
		gethrestime(&xp->xn_ctime);
	}
	return (error);
}