tmp_dir.c revision d5dbd18d69de8954ab5ceb588e99d43fc9b21d46
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/sysmacros.h>
#define T_MUTEX_SIZE 64
{ \
}
void
tmpfs_hash_init(void)
{
int ix;
}
/*
* This routine is where the rubber meets the road for identities.
*/
static void
tmpfs_hash_in(struct tdirent *t)
{
*prevpp = t;
}
/*
* Remove tdirent *t from the hash list.
*/
static void
tmpfs_hash_out(struct tdirent *t)
{
while (*prevpp != t)
}
/*
* Currently called by tdirrename() only.
* rename operation needs to be done with lock held, to ensure that
* no other operations can access the tmpnode at the same instance.
*/
static void
{
}
static struct tdirent *
{
struct tdirent *l;
while (l) {
/*
* We need to make sure that the tmpnode that
* we put a hold on is the same one that we pass back.
* Hence, temporary variable tnp is necessary.
*/
tnp = l->td_tmpnode;
if (hold) {
}
if (found)
return (l);
} else {
l = l->td_link;
}
}
return (NULL);
}
/*
* Search directory 'parent' for entry 'name'.
*
* The calling thread can't hold the write version
* of the rwlock for the directory being searched
*
* 0 is returned on success and *foundtp points
* to the found tmpnode with its vnode held.
*/
int
char *name,
{
int error;
return (ENOTDIR);
return (error);
if (*name == '\0') {
return (0);
}
/*
* Search the directory for the matching name
* We need the lock protecting the tn_dir list
* so that it doesn't change out from underneath us.
* tmpfs_hash_lookup() will pass back the tmpnode
* with a hold on it.
*/
return (0);
}
return (ENOENT);
}
/*
* Enter a directory entry for 'name' and 'tp' into directory 'dir'
*
* Returns 0 on success.
*/
int
char *name, /* name of entry */
{
int error = 0;
char *s;
/*
* tn_rwlock is held to serialize direnter and dirdeletes
*/
/*
* Don't allow '/' characters in pathname component
* (thus in ufs_direnter()).
*/
for (s = name; *s; s++)
if (*s == '/')
return (EACCES);
if (name[0] == '\0')
panic("tdirenter: NULL name");
/*
* For link and rename lock the source entry and check the link count
* to see if it has been removed while it was unlocked.
*/
return (ENOENT);
}
return (EMLINK);
}
}
/*
* 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.
*/
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.
*/
goto out;
}
if ((fromparent != dir) &&
goto out;
}
}
}
/*
* Search for the entry. Return "found" if it exists.
*/
if (tdp) {
switch (op) {
case DE_CREATE:
case DE_MKDIR:
if (tpp) {
} else {
}
break;
case DE_RENAME:
if (error == 0) {
}
break;
case DE_LINK:
/*
* Can't link to an existing file.
*/
break;
}
} else {
/*
* The entry does not exist. Check write permission in
* directory to see if entry can be created.
*/
goto out;
/*
* Make new tmpnode and directory entry as required.
*/
if (error)
goto out;
}
/*
* Unmake the inode we just made.
*/
/*
* cleanup allocs made by tdirinit()
*/
}
}
} else if (tpp) {
}
}
}
out:
/*
* Undo bumped link count.
*/
}
return (error);
}
/*
* Delete entry tp of name "nm" from dir.
* Free dir entry space and decrement link count on tmpnode(s).
*
* Return 0 on success.
*/
int
char *nm,
{
int error;
if (nm[0] == '\0')
/*
* return error when removing . and ..
*/
if (nm[0] == '.') {
return (EINVAL);
return (EEXIST); /* thus in ufs */
}
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.
*/
return (error);
return (ENOENT);
/*
* If it is gone, some other thread got here first!
* Return error ENOENT.
*/
return (ENOENT);
}
/*
* If the tmpnode in the tdirent changed, we were probably
* the victim of a concurrent rename operation. The original
* is gone, so return that status (same as UFS).
*/
return (ENOENT);
/*
* Take tpdp out of the directory list.
*/
}
}
/*
* If the roving slot pointer happens to match tpdp,
* point it at the previous dirent.
*/
}
/*
* tpdp points to the correct directory entry
*/
dir->tn_dirents--;
gethrestime(&now);
}
return (0);
}
/*
* tdirinit is used internally to initialize a directory (dir)
* with '.' and '..' entries without checking permissions and locking
*/
void
{
/*
* Initialize the entries
*/
/*
* Initialize directory entry list.
*/
gethrestime(&now);
/*
* Link counts are special for the hidden attribute directory.
* The only explicit reference in the name space is "." and
* the reference through ".." is not counted on the parent
* file. The attrdir is created as a side effect to lookup,
* so don't change the ctime of the parent.
* Since tdirinit is called with both dir and parent being the
* same for the root vnode, we need to increment this before we set
* tn_nlink = 2 below.
*/
}
}
/*
* tdirtrunc is called to remove all directory entries under this directory.
*/
void
{
/*
* Adjust the link counts to account for this directory
* entry removal. Hidden attribute directories may
* not be empty as they may be truncated as a side-
* operations to free up these tmpnodes.
*
* Skip the link count adjustment for parents of
* attribute directories as those link counts
* do not include the ".." reference in the hidden
* directories.
*/
if (!skip_decr) {
}
dir->tn_dirents--;
}
gethrestime(&now);
}
/*
* Check if the source directory is in the path of the target directory.
* The target directory is locked by the caller.
*
* XXX - The source and target's should be different upon entry.
*/
static int
{
int error = 0;
return (ENOENT);
/* root of fs. search trivially satisfied. */
return (0);
}
for (;;) {
/*
* Return error for cases like "mv c c/d",
* "mv c c/d/e" and so on.
*/
break;
}
if (error) {
break;
}
/*
* We're okay if we traverse the directory tree up to
* the root directory and don't run into the
* parent directory.
*/
break;
}
}
return (error);
}
static int
char *nm, /* entry we are trying to change */
{
int error = 0;
int doingdirectory;
#if defined(lint)
#endif
/*
* Short circuit rename of something to itself.
*/
return (ESAME); /* special KLUDGE error code */
/*
* Check that everything is on the same filesystem.
*/
goto out;
}
/*
* Must have write permission to rewrite target entry.
* Check for stickyness.
*/
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.
*/
if (!doingdirectory) {
goto out;
}
/*
* vn_vfswlock will prevent mounts from using the directory
* until we are done.
*/
goto out;
}
goto out;
}
/*
* Update atime because checking tn_dirents is
* logically equivalent to reading the directory
*/
goto out;
}
} else if (doingdirectory) {
goto out;
}
gethrestime(&now);
/*
* Upgrade to write lock on "to" (i.e., the target tmpnode).
*/
/*
* Decrement the link count of the target tmpnode.
*/
if (doingdirectory) {
/*
* The entry for "to" no longer exists so release the vfslock.
*/
/*
* Decrement the target link count and delete all entires.
*/
/*
* 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)
}
out:
return (error);
}
static void
{
/*
* Increment the link count in the new parent tmpnode
*/
/*
* Decrement the link count of the old parent tmpnode.
* If fromparent is NULL, then this is a new directory link;
* it has no parent, so we need not do anything.
*/
if (fromparent != NULL) {
if (fromparent->tn_nlink != 0) {
fromparent->tn_nlink--;
}
}
}
static int
char *name,
{
/*
* Make sure the parent directory wasn't removed from
* underneath the caller.
*/
return (ENOENT);
/*
* Check that everything is on the same filesystem.
*/
return (EXDEV);
/*
* Allocate and initialize directory entry
*/
return (ENOSPC);
dir->tn_dirents++;
/*
* The directory entry and its name were allocated sequentially.
*/
/*
* Some utilities expect the size of a directory to remain
* somewhat static. For example, a routine which unlinks
* files between calls to readdir(); the size of the
* directory changes from underneath it and so the real
* directory offset in bytes is invalid. To circumvent
* this problem, we initialize a directory entry with an
* phony offset, and use this offset to determine end of
* file in tmp_readdir.
*/
/*
* Install at first empty "slot" in directory list.
*/
}
/*
* If we're at the end of the dirent list and the offset (which
* is necessarily the largest offset in this directory) is more
* than twice the number of dirents, that means the directory is
* 50% holes. At this point we reset the slot pointer back to
* the beginning of the directory so we start using the holes.
* The idea is that if there are N dirents, there must also be
* N holes, so we can satisfy the next N creates by walking at
* most 2N entries; thus the average cost of a create is constant.
* Note that we use the first dirent's td_prev as the roving
* slot pointer; it's ugly, but it saves a word in every dirent.
*/
else
}
gethrestime(&now);
return (0);
}
static int
{
return (EOVERFLOW);
/* parent dir is , mark file as xattr */
}
} else {
}
/*
* To determine the group-id of the created file:
* 1) If the gid is set in the attribute list (non-Sun & pre-4.0
* clients are not likely to set the gid), then use it if
* the process is privileged, belongs to the target group,
* or the group is the same as the parent directory.
* 2) If the filesystem was not mounted with the Old-BSD-compatible
* GRPID option, and the directory's set-gid bit is clear,
* then use the process's gid.
* 3) Otherwise, set the group-id to the gid of the parent directory.
*/
secpolicy_vnode_create_gid(cred) == 0)) {
/*
* XXX - is this only the case when a 4.0 NFS client, or a
* client derived from that code, makes a call over the wire?
*/
} else {
else
}
/*
* If we're creating a directory, and the parent directory has the
* set-GID bit set, set it on the new directory.
* Otherwise, if the user is neither privileged nor a member of the
* file's new group, clear the file's set-GID bit.
*/
else {
}
return (0);
}