udf_inode.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* 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 2004 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>
extern struct vnodeops *udf_vnodeops;
/*
* udf_vfs list manipulation routines
*/
struct udf_vfs *udf_vfs_instances;
#ifndef __lint
#endif
#define UD_BEGIN 0x0
#define UD_END 0x1
#define UD_UNKN 0x2
#ifndef __lint
#endif
int32_t ud_cur_inodes = 0;
#ifndef __lint
#endif
uid_t ud_default_uid = 0;
#ifdef DEBUG
struct ud_inode *
{
return (ip);
}
}
return (0);
}
#endif
/* ARGSUSED */
int
{
struct file_entry *fe;
struct ext_attr_hdr *eah;
ud_printf("ud_iget\n");
old_prn = 0;
ftype = 0;
loop:
}
}
return (0);
}
}
/*
* We don't have it in the cache
* Allocate a new entry
*/
if (ud_cur_inodes > ud_max_inodes) {
while (udf_ifreeh == NULL ||
/*
* Try to put an inode on the freelist that's
* sitting in the dnlc.
*/
if (!purged) {
break;
}
}
}
/*
* If there's a free one available and it has no pages attached
* take it. If we're over the high water mark, take it even if
* it has attached pages. Otherwise, make a new one.
*/
if (udf_ifreeh &&
ud_cur_inodes >= ud_max_inodes)) {
ip = udf_ifreeh;
}
return (EINVAL);
}
/*
* We call udf_syncip() to synchronously destroy all pages
* associated with the vnode before re-using it. The pageout
* thread may have beat us to this page so our v_count can
* be > 0 at this point even though we are on the freelist.
*/
goto loop;
}
}
}
/*
* The pageout thread may not have had a chance to release
* its hold on the vnode (if it was active with this vp),
* but the pages should all be invalidated.
*/
} else {
/*
* Try to get memory for this inode without blocking.
* If we can't and there is something on the freelist,
* go ahead and use it, otherwise block waiting for
* memory holding the hash_lock. We expose a potential
* deadlock if all users of memory have to do a ud_iget()
* before releasing memory.
*/
if (udf_ifreeh) {
nomem = 1;
goto tryagain;
} else {
sizeof (struct ud_inode),
KM_SLEEP);
}
}
}
}
return (EINVAL);
}
if (vn_has_cached_data(vp)) {
}
return (EINVAL);
}
/*
* Move the inode on the chain for its new (ino, dev) pair
*/
/*
* assumption is that we will not
* create a 4096 file
*/
} else {
}
/*
* Check I/O errors
*/
(ftype == STRAT_TYPE4096)) {
/*
* restore old file entry location
*/
/*
* reread old file entry
*/
fe = (struct file_entry *)
1,
udf_vfsp->udf_lbsize) == 0) {
goto end_4096;
}
}
}
}
/*
* The inode may not contain anything useful. Mark it as
* having an error and let anyone else who was waiting for
* this know there was an error. Callers waiting for
* access to this inode in ud_iget will find
* the i_icb_lbano == 0, so there won't be a match.
* It remains in the cache. Put it back on the freelist.
*/
ip->i_icb_lbano = 0;
/*
* The folowing two lines make
* it impossible for any one do
* a VN_HOLD and then a VN_RELE
* so avoiding a ud_iinactive
*/
ip->i_icb_block = 0;
/*
* remove the bad inode from hash chains
* so that during unmount we will not
* go through this inode
*/
/* Put the inode at the front of the freelist */
return (EIO);
}
struct indirect_entry *ie;
/*
* save old file_entry location
*/
/*
* If astrat is 4096 different versions
* of the file exist on the media.
* we are supposed to get to the latest
* version of the file
*/
/*
* IE is supposed to be in the next block
* of DE
*/
/*
* Get rid of current ibp and
* then goto error on DE's bp
*/
goto error_ret;
}
1, &dummy);
goto read_de;
}
/*
* If this block is TE or unrecorded we
* are at the last entry
*/
/*
* This is not an unrecorded block
* Check if it a valid IE and
* get the address of DE that
* this IE points to
*/
goto ie_error;
}
/*
* If ud_check_unrec returns "0"
* this is the last in the chain
* Latest file_entry
*/
}
}
}
}
1,
sizeof (struct file_entry) -
/*
* We now check the validity of ea_off.
* (ea_len - ea_off) should be large enough to
* hold the attribute header atleast.
*/
sizeof (struct attr_hdr)) {
"ea_len(0x%x) - ea_off(0x%x) is too small to hold attr. info. blockno 0x%x\n",
goto error_ret;
}
/*
* Device Specification EA
*/
struct dev_spec_ear *ds;
sizeof (struct dev_spec_ear)) {
"ea_len(0x%x) - ea_off(0x%x) is too small to hold dev_spec_ear. blockno 0x%x\n",
goto error_ret;
}
}
/*
* Impl Use EA
*/
struct copy_mgt_info *cmi;
sizeof (struct iu_ea)) {
"ea_len(0x%x) - ea_off(0x%x) is too small to hold iu_ea. blockno 0x%x\n",
ip->i_icb_block);
goto error_ret;
}
== 0) {
/* skip it */
== 0) {
cmi = (struct copy_mgt_info *)
}
}
/* ??? PARANOIA */
break;
}
}
}
}
/* Strictly Paranoia */
/* Short allocation desc */
ip->i_ext_used = 0;
ip->i_cur_max_ext --;
goto error_ret;
}
while (ndesc --) {
if ((length & 0x3FFFFFFF) == 0) {
break;
}
ip->i_con_used = 0;
ip->i_con_read = 0;
ip->i_con_count *
sizeof (struct icb_ext),
KM_SLEEP);
}
ip->i_con_used++;
sad ++;
break;
}
(~udf_vfsp->udf_lbmask);
ip->i_ext_used++;
iext++;
sad ++;
}
/* Long allocation desc */
ip->i_ext_used = 0;
ip->i_cur_max_ext --;
goto error_ret;
}
while (ndesc --) {
if ((length & 0x3FFFFFFF) == 0) {
break;
}
ip->i_con_used = 0;
ip->i_con_read = 0;
ip->i_con_count *
sizeof (struct icb_ext),
KM_SLEEP);
}
ip->i_con_used++;
lad ++;
break;
}
(~udf_vfsp->udf_lbmask);
ip->i_ext_used++;
iext++;
lad ++;
}
goto error_ret;
}
} else {
/* Not to be used in UDF 1.50 */
ip->i_desc_type);
goto error_ret;
}
if (icb_tag_flags & ICB_FLAG_SETUID) {
} else {
}
if (icb_tag_flags & ICB_FLAG_SETGID) {
}
if (icb_tag_flags & ICB_FLAG_STICKY) {
}
case FTYPE_DIRECTORY :
break;
case FTYPE_FILE :
break;
case FTYPE_BLOCK_DEV :
break;
case FTYPE_CHAR_DEV :
break;
case FTYPE_FIFO :
break;
case FTYPE_C_ISSOCK :
break;
case FTYPE_SYMLINK :
break;
default :
break;
}
}
/*
* Fill in the rest. Don't bother with the vnode lock because nobody
* should be looking at this vnode. We have already invalidated the
* pages if it had any so pageout shouldn't be referencing this vnode
* and we are holding the write contents lock so a look up can't use
* the vnode.
*/
} else {
}
return (0);
}
void
{
ud_printf("ud_iinactive\n");
/*
* Get exclusive access to inode data.
*/
/*
* Make sure no one reclaimed the inode before we put
* it on the freelist or destroy it. We keep our 'hold'
* on the vnode from vn_rele until we are ready to
*
* operation via an async putpage, so we must make sure
* may also put a VN_HOLD on the inode before it grabs
* the i_contents lock. This is done so we don't kmem_free
* an inode that a thread is waiting on.
*/
return;
}
return;
}
/*
* For forced umount case: if i_udf is NULL, the contents of
* the inode and all the pages have already been pushed back
* to disk. It can be safely destroyed.
*/
vn_invalid(vp);
return;
}
/*
* Write the inode out if dirty. Pages are
* written back and put on the freelist.
*/
/*
* Do nothing if inode is now busy -- inode may
* have gone busy because ud_syncip
* releases/reacquires the i_contents lock
*/
return;
}
} else {
}
}
/*
* Put the inode on the end of the free list.
* Possibly in some cases it would be better to
* put the inode at the head of the free list,
* (e.g.: where i_perm == 0 || i_number == 0)
* but I will think about that later.
* (i_number is rarely 0 - only after an i/o error in ud_iget,
* where i_perm == 0, the inode will probably be wanted
* again soon for an ialloc, so possibly we should keep it)
*/
/*
* If inode is invalid or there is no page associated with
* this inode, put the inode in the front of the free list.
* Since we have a VN_HOLD on the vnode, and checked that it
* wasn't already on the freelist when we entered, we can safely
* put it on the freelist even if another thread puts a VN_HOLD
*/
if (vn_has_cached_data(vp)) {
/*
* We're not over our high water mark, or it's
* not safe to kmem_free the inode, so put it
* on the freelist.
*/
if (vn_has_cached_data(vp)) {
}
} else {
if (vn_has_cached_data(vp)) {
}
/*
* Try to free the inode. We must make sure
* it's o.k. to destroy this inode. We can't destroy
* if a thread is waiting for this inode. If we can't get the
* cache now, put it back on the freelist.
*/
if (!mutex_tryenter(&ud_icache_lock)) {
busy = 1;
goto tryagain;
}
/* inode is wanted in ud_iget */
busy = 1;
goto tryagain;
}
}
}
}
void
{
struct file_entry *fe;
ud_printf("ud_iupdat\n");
/*
* Return if file system has been forcibly umounted.
*/
return;
}
return;
}
return;
}
return;
}
}
} else {
tag_flags = 0;
}
}
}
/*
* Remove the following it is no longer contig
* if (ip->i_astrat == STRAT_TYPE4) {
* tag_flags |= ICB_FLAG_CONTIG;
* }
*/
(ip->i_ext_used != 0)) {
}
if (error) {
}
}
} else {
fe->fe_len_adesc = 0;
}
/*
* Zero out the rest of the block
*/
if (waitfor) {
/*
* Synchronous write has guaranteed that inode
* has been written on disk so clear the flag
*/
} else {
/*
* This write hasn't guaranteed that inode has been
* written on the disk.
* Since, all updat flags on indoe are cleared, we must
* remember the condition in case inode is to be updated
* synchronously later (e.g.- fsync()/fdatasync())
* and inode has not been modified yet.
*/
}
} else {
/*
* In case previous inode update was done asynchronously
* (IBDWRITE) and this inode update request wants guaranteed
* (synchronous) disk update, flush the inode.
*/
}
}
}
{
} else {
/* This cannot happen return */
return (EINVAL);
}
} else {
}
con_index = 0;
} else {
if (index == 0) {
/*
* bp is already read
* First few extents will go
* into the file_entry
*/
fe->fe_len_adesc =
/*
* Last entry to be cont ext
*/
} else {
/*
* Read the buffer
*/
return (EIO);
}
/*
* Figure out how many extents in
* this time
*/
sizeof (struct alloc_ext_desc)) / elen;
} else {
count --;
}
con_index++;
} else {
}
}
/*
* convert to on disk form and
* update
*/
if (index != 0) {
sizeof (struct alloc_ext_desc));
}
}
} else {
if (index != 0) {
sizeof (struct alloc_ext_desc));
}
}
}
if (con_index != 0) {
struct alloc_ext_desc *aed;
}
if (con_index == 1) {
aed->aed_rev_ael =
} else {
aed->aed_rev_ael =
}
sz += sizeof (struct alloc_ext_desc);
}
/*
* Write back to disk
*/
}
}
}
/*
* Free unused continuation extents
*/
}
}
return (0);
}
/* ARGSUSED */
{
return (ENXIO);
}
void
{
if (count != 0) {
sad++;
iext++;
index++;
}
}
}
void
{
if (count != 0) {
lad++;
iext++;
index++;
}
}
}
/*
* Truncate the inode ip to at most length size.
* Free affected disk blocks -- the blocks of the
* file are removed in reverse order.
*/
/* ARGSUSED */
int
{
ud_printf("ud_itrunc\n");
/*
* We only allow truncation of regular files and directories
* to arbritary lengths here. In addition, we allow symbolic
* links to be truncated only to zero length. Other inode
* types cannot have their length set here.
*/
return (0);
}
return (EINVAL);
}
/* update ctime and mtime to please POSIX tests */
return (0);
}
/*
* Trunc up case.ud_bmap_write will insure that the right blocks
* are allocated. This includes doing any work needed for
* allocating the last block.
*/
if (boff == 0) {
} else {
}
if (error == 0) {
/*
* Make sure we zero out the remaining bytes of
* the page in case a mmap scribbled on it. We
* can't prevent a mmap from writing beyond EOF
* on the last page of a file.
*/
}
}
return (error);
}
/*
* Update the pages of the file. If the file is not being
* truncated to a block boundary, the contents of the
* pages following the end of the file must be zero'ed
* in case it ever become accessable again because
* of subsequent file growth.
*/
if (boff == 0) {
} else {
/*
* Make sure that the last block is properly allocated.
* We only really have to do this if the last block is
* actually allocated. Just to be sure, we do it now
* independent of current allocation.
*/
if (error) {
return (error);
}
}
/* Free the blocks */
return (EFBIG);
}
} else {
return (error);
}
}
}
done:
return (0);
}
void
{
if (ip->i_ext_used == 0) {
return;
}
/*
* Find the begining and end
* of current extent
*/
/*
* This is the extent that has offset "length"
* make a copy of this extent and
* remember the index. We can use
* it to free blocks
*/
break;
}
}
}
}
}
/*
* Free the unused space
*/
if (count) {
}
}
}
continue;
}
/*
* release any continuation blocks
*/
/*
* Find out how many indirect blocks
* are required and release the rest
*/
}
} else {
ecount = 0;
}
con_freed = 0;
(sizeof (struct alloc_ext_desc) + elen);
/* Header + 1 indirect extent */
if (ecount) {
} else {
ecount = 0;
}
} else {
count);
con_freed++;
}
}
/*
* set the continuation extents used(i_con_used)i to correct
* value. It is possible for i_con_used to be zero,
* if we free up all continuation extents. This happens
* when ecount is 0 before entering the for loop above.
*/
}
}
}
void
{
/*
* Truncate code is the same for
* both file of type 4 and 4096
*/
}
/*
* Remove any inodes in the inode cache belonging to dev
*
* There should not be any active ones, return error if any are found but
* still invalidate others (N.B.: this is a user error, not a system error).
*
* Also, count the references to dev by block devices - this really
* has nothing to do with the object of the procedure, but as we have
* to scan the inode table here anyway, we might as well get the
* extra benefit.
*/
{
ud_printf("ud_iflush\n");
continue;
}
/*
* root inode is processed by the caller
*/
busy = -1;
}
continue;
}
/*
* Set error indicator for return value,
* but continue invalidating other
* inodes.
*/
busy = -1;
continue;
}
/*
* Hold the vnode since its not done
* in VOP_PUTPAGE anymore.
*/
/*
* XXX Synchronous write holding
* cache lock
*/
}
}
return (busy);
}
/*
* Check mode permission on inode. Mode is READ, WRITE or EXEC.
* In the case of WRITE, the read-only status of the file system
* is checked. The applicable mode bits are compared with the
* requested form of access. If bits are missing, the secpolicy
* function will check for privileges.
*/
int
{
int shift = 0;
/*
* ASSERT(RW_READ_HELD(&ip->i_contents));
*/
ud_printf("ud_iaccess\n");
/*
* Disallow write attempts on read-only
* file systems, unless the file is a block
* or character device or a FIFO.
*/
return (EROFS);
}
}
}
/*
* Access check is based on only
* one of owner, group, public.
* If not owner, then check group.
* If not a member of the group, then
* check public access.
*/
shift += 5;
shift += 5;
}
if (mode == 0)
return (0);
UD2VA_PERM(mode)));
}
void
{
gethrestime(&now);
ud_printf("ud_imark\n");
}
}
}
}
void
{
ud_printf("ud_itimes_nolock\n");
} else {
}
}
}
void
{
ud_printf("ud_delcache\n");
}
void
{
ud_printf("ud_idrop\n");
return;
}
/*
* if inode is invalid or there is no page associated with
* this inode, put the inode in the front of the free list
*/
} else {
/*
* Otherwise, put the inode back on the end of the free list.
*/
}
}
void
{
#ifdef DEBUG
/* Search if the element is already in the list */
if (udf_ifreeh != NULL) {
iq = udf_ifreeh;
while (iq) {
}
}
}
#endif
if (udf_ifreeh == NULL) {
/*
* Nothing on the list just add it
*/
udf_ifreeh = ip;
udf_ifreet = ip;
} else {
/*
* Add at the begining of the list
*/
udf_ifreeh = ip;
} else {
/*
* Add at the end of the list
*/
udf_ifreet = ip;
}
}
}
void
{
#ifdef DEBUG
{
iq = udf_ifreeh;
while (iq) {
found++;
}
}
if (found != 1) {
}
}
#endif
if (ip != udf_ifreeh) {
return;
}
}
(ip == udf_ifreeh)) {
udf_ifreet = NULL;
} else {
}
} else {
} else {
}
}
}
void
ud_init_inodes(void)
{
int index;
#ifndef __lint
#endif
}
udf_ifreeh = NULL;
udf_ifreet = NULL;
#ifndef __lint
#endif
}