udf_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>
#include <sys/resource.h>
#include <sys/pathname.h>
#include <sys/bootconf.h>
#include <vm/seg_kmem.h>
struct slot {
int size; /* size of area at slotoffset */
};
struct ud_inode *);
int
{
ud_printf("ud_dirlook\n");
doingchk = 0;
old_prn = 0xFFFF;
old_loc = 0;
/*
* Check accessibility of directory.
*/
return (ENOTDIR);
}
return (error);
}
/*
* Null component name is synonym for directory being searched.
*/
if (*namep == '\0') {
return (0);
}
if ((namelen == 1) &&
/* Current directory */
return (0);
}
/* vp is already held from dnlc_lookup */
return (0);
}
/*
* Read lock the inode we are searching. You will notice that we
* didn't hold the read lock while searching the dnlc. This means
* that the entry could now be in the dnlc. This doesn't cause any
* problems because dnlc_enter won't add an entry if it is already
* there.
*/
/*
* Take care to look at dip->i_diroff only once, as it
*/
offset = 0;
}
while (adhoc_search--) {
if (error != 0) {
break;
}
id_len = 2;
dummy[0] = '.';
} else {
if ((error = ud_uncompress(
break;
}
}
namelen) == 0)) {
if (doingchk) {
goto checkok;
} else {
S_READ);
}
goto restart;
}
/* NOTREACHED */
}
if (namelen == 2 &&
fname[0] == '.' &&
struct timespec32 omtime;
if (error) {
goto done;
}
doingchk = 1;
S_READ);
}
goto recheck;
}
} else {
}
if (error == 0) {
}
goto done;
}
}
}
}
end = adhoc_offset;
offset = 0;
}
done:
}
return (error);
}
int
{
char *s;
ud_printf("ud_direnter\n");
/* don't allow '/' characters in pathname component */
if (*s == '/') {
return (EACCES);
}
}
if (namlen == 0) {
return (EINVAL);
}
/*
* If name is "." or ".." then if this is a create look it up
* and return EEXIST. Rename or link TO "." or ".." is forbidden.
*/
if (namep[0] == '.' &&
return (EINVAL); /* *SIGH* should be ENOTEMPTY */
}
if (ipp) {
/*
* ud_dirlook will acquire the i_rwlock
*/
return (err);
}
}
return (EEXIST);
}
/*
* 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 not, we
* increment the link count and force the inode to disk to make sure
* that it is there before any directory entry that points to it.
*/
return (ENOENT);
}
return (EMLINK);
}
}
/*
* If target directory has not been removed, then we can consider
* allowing file to be created.
*/
goto out2;
}
/*
* Check accessibility of directory.
*/
goto out2;
}
/*
* Execute access is required to search the directory.
*/
goto out2;
}
/*
* 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. Also the user must have
* write permission in the source so as to be able to
* change "..".
*/
goto out2;
}
goto out2;
}
goto out2;
}
} else {
}
}
/*
* Search for the entry. Return VN_HELD tip if found.
*/
goto out;
}
if (tip) {
switch (op) {
case DE_CREATE :
case DE_MKDIR :
if (ipp) {
} else {
}
break;
case DE_RENAME :
/*
* We used to VN_RELE() here, but this
* was moved down so that we could send
* a vnevent after the locks were dropped.
*/
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 inode and directory entry as required.
*/
goto out;
}
/*
* Unmake the inode we just made.
*/
}
}
} else if (ipp) {
}
}
out:
}
}
/*
* If it's all good, send events after locks are dropped
* but before vnodes are released.
*/
if (err == 0) {
if (tip) {
}
}
/*
* The following VN_RELE() was moved from the
* DE_RENAME case above
*/
if (tip) {
}
}
out2:
/*
* Undo bumped link count.
*/
}
return (err);
}
/*
* Locking i_contents in this
* function seems to be really weird
*/
int
{
ud_printf("ud_dirremove\n");
if (namelen == 0) {
return (EINVAL);
}
/*
* return err when removing . and ..
*/
if (namep[0] == '.') {
if (namelen == 1) {
return (EINVAL);
return (EEXIST); /* SIGH should be ENOTEMPTY */
}
}
/*
* Check accessibility of directory.
*/
return (ENOTDIR);
}
/*
* Execute access is required to search the directory.
* Access for write is interpreted as allowing
* deletion of files in the directory.
*/
return (err);
}
goto out_novfs;
}
goto out_novfs;
}
goto out_novfs;
}
/*
* vn_vfswlock() prevents races between mount and rmdir.
*/
goto out_novfs;
}
goto out;
}
/*
* If we are removing a directory, get a lock on it.
* If the directory is empty, it will stay empty until
* we can remove it.
*/
}
/* We must be holding i_contents */
}
goto out;
}
/*
* For rmdir(2), some special checks are required.
* (a) Don't remove any alias of the parent (e.g. ".").
* (b) Don't remove the current directory.
* (c) Make sure the entry is (still) a directory.
* (d) Make sure the directory is empty.
*/
/*
* Directories do not have an
* entry for "." so only one link
* will be there
*/
}
if (err) {
}
goto out;
}
/*
* unlink(2) requires a different check: allow only
* privileged processes to unlink a directory.
*/
goto out;
}
}
/*
* Remove the cache'd entry, if any.
*/
/*
* We can collapse all the directory
* entries that are deleted into one big entry
* but the better way is to
* defer it till next directory entry
* creation. where we can do this
* in a more efficient way
*/
/*
* If this is the last entry
* just truncate the file instead
* of marking it deleted
*/
goto out;
}
} else {
goto out;
}
}
/*
* If we were removing a directory, it is 'gone' now so we can
* unlock it.
*/
}
if (err != 0) {
goto out;
}
/*
* Now dispose of the inode.
*/
/*
* Decrement by 1 because there is no "."
* Clear the inode, but there may be other hard
* links so don't free the inode.
* Decrement the dp linkcount because we're
* trashing the ".." entry.
*/
/*
* (void) ud_itrunc(ip, 0, 0, cr);
*/
} else {
}
}
out:
}
}
if (ip) {
/*
* If no errors, send any events after locks are dropped,
* but before the VN_RELE().
*/
if (err == 0) {
}
}
}
return (err);
}
int
{
ud_printf("ud_dircheckforname\n");
offset = 0;
goto end;
}
}
break;
}
break;
}
/* Check for name match */
0)) ||
(namep[0] == '.' &&
(namelen == 1 ||
} else {
goto end;
}
}
goto end;
}
} else {
/*
* see if we need to find an
* empty slot and the current slot
* matches
*/
(matched == 0)) {
}
if (matched == 0) {
namelen) == 0)) {
matched = 1;
}
}
}
}
}
if (fbp) {
}
/*
* We didn't find a slot; the new directory entry should be put
* at the end of the directory. Return an indication of where
* this is, and set "endoff" to zero; since we're going to have
* to extend the directory, we're certainly not going to
* trucate it.
*/
} else {
}
}
end:
return (error);
}
/*
* Return 1 if the dir has all files
* deleted except the parent
* else return 0
*/
/* ARGSUSED */
int
{
ud_printf("ud_dirempty\n");
return (empty);
}
desc_len = 1024;
/*
* First read fid
* and verify checksum
*/
empty = 0;
break;
}
empty = 0;
break;
}
/*
* We verify the tag id and also the FID_LEN.
* FID_LEN should be <= desc_len.
*/
/* Corrupted directory */
empty = 0;
break;
}
/*
* Read the fid + iulen + len
* Now verify both checksum andCRC
*/
empty = 0;
break;
}
/*
* Now that the entire decsriptor is read we verify the
* crc.
*/
tbno,
1, rcount) != 0) {
/* Corrupted directory */
empty = 0;
break;
}
/*
* Is the file deleted
*/
empty = 0;
break;
}
}
}
return (empty);
}
int
{
ud_printf("ud_dircheckpath\n");
goto out;
}
goto out;
}
/*
* Search back through the directory tree, using the PARENT entries
* Fail any attempt to move a directory into an ancestor directory.
*/
for (;;) {
break;
}
break;
}
/* IS this a valid file_identifier */
tbno,
break;
}
break;
}
/*
* This cannot happen unless
* something is grossly wrong
* First entry has to be parent
*/
break;
}
if (parent_icb_loc == blkno) {
break;
}
break;
}
}
}
/*
* Race to get the inode.
*/
break;
}
}
if (fbp) {
}
out:
if (ip) {
}
}
return (err);
}
int
{
/*
* Allocate a new inode.
*/
return (error);
}
}
/*
* mtime fields. They were set from the passed in attributes in
* ud_ialloc().
*/
/*
* push inode before it's name appears in a directory
*/
return (error);
}
/*
* Enter the file sip in the directory tdp with name namep.
*/
int
{
ud_printf("ud_diraddentry\n");
/*
* Check inode to be linked to see if it is in the
* same filesystem.
*/
goto bad;
}
goto bad;
}
}
/*
* Fill in entry data.
*/
} else {
}
}
bad:
return (error);
}
/*
* Write a prototype directory into the empty inode ip, whose parent is dp.
*/
/* ARGSUSED2 */
int
{
ud_printf("ud_dirmakedirect\n");
parent_len = sizeof (struct file_id);
/*
* Allocate space for the directory we're creating.
*/
return (err);
}
/*
* init with the size of
* directory with just the
* parent
*/
} else {
}
/*
* Update the dp link count and write out the change.
* This reflects the ".." entry we'll soon write.
*/
return (EMLINK);
}
/*
* Initialize directory with ".."
* Since the parent directory is locked, we don't have to
* worry about anything changing when we drop the write
* lock on (ip).
*/
return (err);
}
/*
* fid_idlen, fid_iulen and fid_spec are zero
* due to bzero above
*/
}
return (err);
}
int
{
ud_printf("ud_dirrename\n");
/*
* Short circuit rename of something to itself.
*/
return (ESAME); /* special KLUDGE error code */
}
/*
* Everything is protected under the vfs_rename_lock so the ordering
* of i_contents locks doesn't matter here.
*/
/*
* Check that everything is on the same filesystem.
*/
goto out;
}
/*
* Must have write permission to rewrite target entry.
*/
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;
}
goto out;
}
} else if (doingdirectory) {
goto out;
}
/*
* Rewrite the inode pointer for target name entry
* from the target inode (ip) to the source inode (sip).
* This prevents the target entry from disappearing
* during a crash. Mark the directory inode to reflect the changes.
*/
if (error) {
if (doingdirectory) {
}
goto out;
}
/*
* Upgrade to write lock on tip
*/
/*
* Decrement the link count of the target inode.
* Fix the ".." entry in sip to point to dp.
* This is done after the new entry is on the disk.
*/
if (doingdirectory) {
/*
* The entry for tip no longer exists so I can unlock the
* vfslock.
*/
/*
* Decrement target link count once more if it was a directory.
*/
"ud_direnter: target directory link count != 0");
return (EINVAL);
}
/*
* 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. We decrement the link
* count in the new parent as appropriate to reflect
* the just-removed target. If the parent is the
* same, this is appropriate since the original
* directory is going away. If the new parent is
* different, dirfixdotdot() will bump the link count
* back.
*/
return (error);
}
}
out:
return (error);
}
/*
* 1. When we find a slot that belonged to a file which was deleted
* and is in the middle of the directory
* 2. There is not empty slot available. The new entry
* will be at the end of the directory and fits in the same block.
* 3. There is no empty slot available. The new
* entry will not fit the left over directory
* so we need to allocate a new block. If
* we cannot allocate a proximity block we need
* to allocate a new icb, and data block.
*/
int
{
ud_printf("ud_dirprepareentry\n");
/*
* If we didn't find a slot, then indicate that the
* new slot belongs at the end of the directory.
* If we found a slot, then the new entry can be
* put at slotp->offset.
*/
/*
* We did not find a slot, the next
* entry will be in the end of the directory
* see if we can fit the new entry inside
* the old block. If not allocate a new block.
*/
/*
* extend the directory
* size by one new block
*/
0, cr);
if (error != 0) {
return (error);
}
/*
* oops we changed the astrat
* of the file, we have to
* recaliculate tags
* fortunately we donot have more
* than one lbsize to handle here
*/
0, &tbno)) != 0) {
return (error);
}
return (error);
}
off = 0;
}
return (error);
}
}
} else {
/* Extend the directory size */
}
}
return (EINVAL);
}
return (error);
}
/*
* fbread cannot cross a
* MAXBSIZE boundary so handle it here
*/
return (error);
}
} else {
}
return (error);
}
return (error);
}
return (error);
}
}
return (error);
}
/*
* Fix the FID_PARENT entry of the child directory so that it points
* to the new parent directory instead of the old one. Routine
* assumes that dp is a directory and that all the inodes are on
* the same file system.
*/
int
{
ud_printf("ud_dirfixdotdot\n");
goto bad;
}
goto bad;
}
tbno,
(FID_DIR | FID_PARENT))) {
goto bad;
}
goto bad;
}
/*
* Increment the link count in the new parent inode and force it out.
*/
goto bad;
}
/*
* Rewrite the child FID_PARENT entry and force it out.
*/
if (err != 0) {
goto bad;
}
/*
* Decrement the link count of the old parent inode and force
* it out. If opdp is NULL, then this is a new directory link;
* it has no parent, so we need not do anything.
*/
}
}
return (0);
bad:
if (fbp) {
}
return (err);
}
{
goto out;
}
/*
* We do not need to write the
* file name. So check if the entry
* does not cross a block boundary
* and write only required portions
*/
sizeof (struct file_id)) &
} else {
old_count = 0;
}
} else {
sizeof (struct file_id));
}
} else {
}
}
out:
return (error);
}