diff options
Diffstat (limited to '')
-rw-r--r-- | fs/coda/upcall.c | 499 |
1 files changed, 230 insertions, 269 deletions
diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c index 5faacdb1a479..cdb4c07a7870 100644 --- a/fs/coda/upcall.c +++ b/fs/coda/upcall.c @@ -35,12 +35,10 @@ #include <linux/coda_psdev.h> #include <linux/coda_fs_i.h> #include <linux/coda_cache.h> -#include <linux/coda_proc.h> -#define upc_alloc() kmalloc(sizeof(struct upc_req), GFP_KERNEL) -#define upc_free(r) kfree(r) +#include "coda_int.h" -static int coda_upcall(struct coda_sb_info *mntinfo, int inSize, int *outSize, +static int coda_upcall(struct venus_comm *vc, int inSize, int *outSize, union inputArgs *buffer); static void *alloc_upcall(int opcode, int size) @@ -86,13 +84,9 @@ int venus_rootfid(struct super_block *sb, struct CodaFid *fidp) insize = SIZE(root); UPARG(CODA_ROOT); - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - - if (error) { - printk("coda_get_rootfid: error %d\n", error); - } else { + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + if (!error) *fidp = outp->coda_root.VFid; - } CODA_FREE(inp, insize); return error; @@ -109,9 +103,9 @@ int venus_getattr(struct super_block *sb, struct CodaFid *fid, UPARG(CODA_GETATTR); inp->coda_getattr.VFid = *fid; - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - - *attr = outp->coda_getattr.attr; + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + if (!error) + *attr = outp->coda_getattr.attr; CODA_FREE(inp, insize); return error; @@ -130,7 +124,7 @@ int venus_setattr(struct super_block *sb, struct CodaFid *fid, inp->coda_setattr.VFid = *fid; inp->coda_setattr.attr = *vattr; - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); CODA_FREE(inp, insize); return error; @@ -156,64 +150,18 @@ int venus_lookup(struct super_block *sb, struct CodaFid *fid, memcpy((char *)(inp) + offset, name, length); *((char *)inp + offset + length) = '\0'; - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - - *resfid = outp->coda_lookup.VFid; - *type = outp->coda_lookup.vtype; - - CODA_FREE(inp, insize); - return error; -} - -int venus_store(struct super_block *sb, struct CodaFid *fid, int flags, - vuid_t uid) -{ - union inputArgs *inp; - union outputArgs *outp; - int insize, outsize, error; -#ifdef CONFIG_CODA_FS_OLD_API - struct coda_cred cred = { 0, }; - cred.cr_fsuid = uid; -#endif - - insize = SIZE(store); - UPARG(CODA_STORE); - -#ifdef CONFIG_CODA_FS_OLD_API - memcpy(&(inp->ih.cred), &cred, sizeof(cred)); -#else - inp->ih.uid = uid; -#endif - - inp->coda_store.VFid = *fid; - inp->coda_store.flags = flags; - - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - - CODA_FREE(inp, insize); - return error; -} - -int venus_release(struct super_block *sb, struct CodaFid *fid, int flags) -{ - union inputArgs *inp; - union outputArgs *outp; - int insize, outsize, error; - - insize = SIZE(release); - UPARG(CODA_RELEASE); - - inp->coda_release.VFid = *fid; - inp->coda_release.flags = flags; - - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + if (!error) { + *resfid = outp->coda_lookup.VFid; + *type = outp->coda_lookup.vtype; + } CODA_FREE(inp, insize); return error; } int venus_close(struct super_block *sb, struct CodaFid *fid, int flags, - vuid_t uid) + vuid_t uid) { union inputArgs *inp; union outputArgs *outp; @@ -235,7 +183,7 @@ int venus_close(struct super_block *sb, struct CodaFid *fid, int flags, inp->coda_close.VFid = *fid; inp->coda_close.flags = flags; - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); CODA_FREE(inp, insize); return error; @@ -251,12 +199,12 @@ int venus_open(struct super_block *sb, struct CodaFid *fid, insize = SIZE(open_by_fd); UPARG(CODA_OPEN_BY_FD); - inp->coda_open.VFid = *fid; - inp->coda_open.flags = flags; - - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); + inp->coda_open_by_fd.VFid = *fid; + inp->coda_open_by_fd.flags = flags; - *fh = outp->coda_open_by_fd.fh; + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + if (!error) + *fh = outp->coda_open_by_fd.fh; CODA_FREE(inp, insize); return error; @@ -281,11 +229,12 @@ int venus_mkdir(struct super_block *sb, struct CodaFid *dirfid, /* Venus must get null terminated string */ memcpy((char *)(inp) + offset, name, length); *((char *)inp + offset + length) = '\0'; - - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - *attrs = outp->coda_mkdir.attr; - *newfid = outp->coda_mkdir.VFid; + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + if (!error) { + *attrs = outp->coda_mkdir.attr; + *newfid = outp->coda_mkdir.VFid; + } CODA_FREE(inp, insize); return error; @@ -323,7 +272,7 @@ int venus_rename(struct super_block *sb, struct CodaFid *old_fid, memcpy((char *)(inp) + offset, new_name, new_length); *((char *)inp + offset + new_length) = '\0'; - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); CODA_FREE(inp, insize); return error; @@ -351,11 +300,12 @@ int venus_create(struct super_block *sb, struct CodaFid *dirfid, /* Venus must get null terminated string */ memcpy((char *)(inp) + offset, name, length); *((char *)inp + offset + length) = '\0'; - - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - *attrs = outp->coda_create.attr; - *newfid = outp->coda_create.VFid; + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + if (!error) { + *attrs = outp->coda_create.attr; + *newfid = outp->coda_create.VFid; + } CODA_FREE(inp, insize); return error; @@ -377,8 +327,8 @@ int venus_rmdir(struct super_block *sb, struct CodaFid *dirfid, inp->coda_rmdir.name = offset; memcpy((char *)(inp) + offset, name, length); *((char *)inp + offset + length) = '\0'; - - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); + + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); CODA_FREE(inp, insize); return error; @@ -399,8 +349,8 @@ int venus_remove(struct super_block *sb, struct CodaFid *dirfid, inp->coda_remove.name = offset; memcpy((char *)(inp) + offset, name, length); *((char *)inp + offset + length) = '\0'; - - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); + + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); CODA_FREE(inp, insize); return error; @@ -420,19 +370,18 @@ int venus_readlink(struct super_block *sb, struct CodaFid *fid, UPARG(CODA_READLINK); inp->coda_readlink.VFid = *fid; - - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - - if (! error) { - retlen = outp->coda_readlink.count; + + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + if (!error) { + retlen = outp->coda_readlink.count; if ( retlen > *length ) - retlen = *length; + retlen = *length; *length = retlen; result = (char *)outp + (long)outp->coda_readlink.data; memcpy(buffer, result, retlen); *(buffer + retlen) = '\0'; } - + CODA_FREE(inp, insize); return error; } @@ -458,8 +407,8 @@ int venus_link(struct super_block *sb, struct CodaFid *fid, /* make sure strings are null terminated */ memcpy((char *)(inp) + offset, name, len); *((char *)inp + offset + len) = '\0'; - - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); + + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); CODA_FREE(inp, insize); return error; @@ -494,7 +443,7 @@ int venus_symlink(struct super_block *sb, struct CodaFid *fid, memcpy((char *)(inp) + offset, name, len); *((char *)inp + offset + len) = '\0'; - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); CODA_FREE(inp, insize); return error; @@ -509,9 +458,9 @@ int venus_fsync(struct super_block *sb, struct CodaFid *fid) insize=SIZE(fsync); UPARG(CODA_FSYNC); - inp->coda_fsync.VFid = *fid; - error = coda_upcall(coda_sbp(sb), sizeof(union inputArgs), - &outsize, inp); + inp->coda_fsync.VFid = *fid; + error = coda_upcall(coda_vcp(sb), sizeof(union inputArgs), + &outsize, inp); CODA_FREE(inp, insize); return error; @@ -529,7 +478,7 @@ int venus_access(struct super_block *sb, struct CodaFid *fid, int mask) inp->coda_access.VFid = *fid; inp->coda_access.flags = mask; - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); CODA_FREE(inp, insize); return error; @@ -578,9 +527,9 @@ int venus_pioctl(struct super_block *sb, struct CodaFid *fid, goto exit; } - error = coda_upcall(coda_sbp(sb), SIZE(ioctl) + data->vi.in_size, - &outsize, inp); - + error = coda_upcall(coda_vcp(sb), SIZE(ioctl) + data->vi.in_size, + &outsize, inp); + if (error) { printk("coda_pioctl: Venus returns: %d for %s\n", error, coda_f2s(fid)); @@ -620,16 +569,13 @@ int venus_statfs(struct dentry *dentry, struct kstatfs *sfs) insize = max_t(unsigned int, INSIZE(statfs), OUTSIZE(statfs)); UPARG(CODA_STATFS); - error = coda_upcall(coda_sbp(dentry->d_sb), insize, &outsize, inp); - - if (!error) { + error = coda_upcall(coda_vcp(dentry->d_sb), insize, &outsize, inp); + if (!error) { sfs->f_blocks = outp->coda_statfs.stat.f_blocks; sfs->f_bfree = outp->coda_statfs.stat.f_bfree; sfs->f_bavail = outp->coda_statfs.stat.f_bavail; sfs->f_files = outp->coda_statfs.stat.f_files; sfs->f_ffree = outp->coda_statfs.stat.f_ffree; - } else { - printk("coda_statfs: Venus returns: %d\n", error); } CODA_FREE(inp, insize); @@ -638,96 +584,129 @@ int venus_statfs(struct dentry *dentry, struct kstatfs *sfs) /* * coda_upcall and coda_downcall routines. - * */ +static void coda_block_signals(sigset_t *old) +{ + spin_lock_irq(¤t->sighand->siglock); + *old = current->blocked; + + sigfillset(¤t->blocked); + sigdelset(¤t->blocked, SIGKILL); + sigdelset(¤t->blocked, SIGSTOP); + sigdelset(¤t->blocked, SIGINT); + + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); +} + +static void coda_unblock_signals(sigset_t *old) +{ + spin_lock_irq(¤t->sighand->siglock); + current->blocked = *old; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); +} + +/* Don't allow signals to interrupt the following upcalls before venus + * has seen them, + * - CODA_CLOSE or CODA_RELEASE upcall (to avoid reference count problems) + * - CODA_STORE (to avoid data loss) + */ +#define CODA_INTERRUPTIBLE(r) (!coda_hard && \ + (((r)->uc_opcode != CODA_CLOSE && \ + (r)->uc_opcode != CODA_STORE && \ + (r)->uc_opcode != CODA_RELEASE) || \ + (r)->uc_flags & REQ_READ)) -static inline void coda_waitfor_upcall(struct upc_req *vmp, - struct venus_comm *vcommp) +static inline void coda_waitfor_upcall(struct upc_req *req) { DECLARE_WAITQUEUE(wait, current); + unsigned long timeout = jiffies + coda_timeout * HZ; + sigset_t old; + int blocked; - vmp->uc_posttime = jiffies; + coda_block_signals(&old); + blocked = 1; - add_wait_queue(&vmp->uc_sleep, &wait); + add_wait_queue(&req->uc_sleep, &wait); for (;;) { - if ( !coda_hard && vmp->uc_opcode != CODA_CLOSE ) + if (CODA_INTERRUPTIBLE(req)) set_current_state(TASK_INTERRUPTIBLE); else set_current_state(TASK_UNINTERRUPTIBLE); - /* venus died */ - if ( !vcommp->vc_inuse ) - break; - /* got a reply */ - if ( vmp->uc_flags & ( REQ_WRITE | REQ_ABORT ) ) + if (req->uc_flags & (REQ_WRITE | REQ_ABORT)) break; - if ( !coda_hard && vmp->uc_opcode != CODA_CLOSE && signal_pending(current) ) { - /* if this process really wants to die, let it go */ - if ( sigismember(&(current->pending.signal), SIGKILL) || - sigismember(&(current->pending.signal), SIGINT) ) - break; - /* signal is present: after timeout always return - really smart idea, probably useless ... */ - if ( jiffies - vmp->uc_posttime > coda_timeout * HZ ) - break; + if (blocked && time_after(jiffies, timeout) && + CODA_INTERRUPTIBLE(req)) + { + coda_unblock_signals(&old); + blocked = 0; + } + + if (signal_pending(current)) { + list_del(&req->uc_chain); + break; } - schedule(); + + if (blocked) + schedule_timeout(HZ); + else + schedule(); } - remove_wait_queue(&vmp->uc_sleep, &wait); - set_current_state(TASK_RUNNING); + if (blocked) + coda_unblock_signals(&old); - return; + remove_wait_queue(&req->uc_sleep, &wait); + set_current_state(TASK_RUNNING); } -/* - * coda_upcall will return an error in the case of +/* + * coda_upcall will return an error in the case of * failed communication with Venus _or_ will peek at Venus * reply and return Venus' error. * * As venus has 2 types of errors, normal errors (positive) and internal * errors (negative), normal errors are negated, while internal errors * are all mapped to -EINTR, while showing a nice warning message. (jh) - * */ -static int coda_upcall(struct coda_sb_info *sbi, - int inSize, int *outSize, - union inputArgs *buffer) +static int coda_upcall(struct venus_comm *vcp, + int inSize, int *outSize, + union inputArgs *buffer) { - struct venus_comm *vcommp; union outputArgs *out; - struct upc_req *req; + union inputArgs *sig_inputArgs; + struct upc_req *req, *sig_req; int error = 0; - vcommp = sbi->sbi_vcomm; - if ( !vcommp->vc_inuse ) { - printk("No pseudo device in upcall comms at %p\n", vcommp); - return -ENXIO; + if (!vcp->vc_inuse) { + printk(KERN_NOTICE "coda: Venus dead, not sending upcall\n"); + return -ENXIO; } /* Format the request message. */ - req = upc_alloc(); - if (!req) { - printk("Failed to allocate upc_req structure\n"); + req = kmalloc(sizeof(struct upc_req), GFP_KERNEL); + if (!req) return -ENOMEM; - } + req->uc_data = (void *)buffer; req->uc_flags = 0; req->uc_inSize = inSize; req->uc_outSize = *outSize ? *outSize : inSize; req->uc_opcode = ((union inputArgs *)buffer)->ih.opcode; - req->uc_unique = ++vcommp->vc_seq; + req->uc_unique = ++vcp->vc_seq; init_waitqueue_head(&req->uc_sleep); - + /* Fill in the common input args. */ ((union inputArgs *)buffer)->ih.unique = req->uc_unique; /* Append msg to pending queue and poke Venus. */ - list_add_tail(&(req->uc_chain), &vcommp->vc_pending); - - wake_up_interruptible(&vcommp->vc_waitq); + list_add_tail(&req->uc_chain, &vcp->vc_pending); + + wake_up_interruptible(&vcp->vc_waitq); /* We can be interrupted while we wait for Venus to process * our request. If the interrupt occurs before Venus has read * the request, we dequeue and return. If it occurs after the @@ -738,67 +717,60 @@ static int coda_upcall(struct coda_sb_info *sbi, * ENODEV. */ /* Go to sleep. Wake up on signals only after the timeout. */ - coda_waitfor_upcall(req, vcommp); + coda_waitfor_upcall(req); - if (vcommp->vc_inuse) { /* i.e. Venus is still alive */ - /* Op went through, interrupt or not... */ - if (req->uc_flags & REQ_WRITE) { + /* Op went through, interrupt or not... */ + if (req->uc_flags & REQ_WRITE) { out = (union outputArgs *)req->uc_data; /* here we map positive Venus errors to kernel errors */ error = -out->oh.result; *outSize = req->uc_outSize; goto exit; - } - if ( !(req->uc_flags & REQ_READ) && signal_pending(current)) { - /* Interrupted before venus read it. */ - list_del(&(req->uc_chain)); - /* perhaps the best way to convince the app to - give up? */ - error = -EINTR; + } + + error = -EINTR; + if ((req->uc_flags & REQ_ABORT) || !signal_pending(current)) { + printk(KERN_WARNING "coda: Unexpected interruption.\n"); goto exit; - } - if ( (req->uc_flags & REQ_READ) && signal_pending(current) ) { - /* interrupted after Venus did its read, send signal */ - union inputArgs *sig_inputArgs; - struct upc_req *sig_req; - - list_del(&(req->uc_chain)); - error = -ENOMEM; - sig_req = upc_alloc(); - if (!sig_req) goto exit; - - CODA_ALLOC((sig_req->uc_data), char *, sizeof(struct coda_in_hdr)); - if (!sig_req->uc_data) { - upc_free(sig_req); - goto exit; - } - - error = -EINTR; - sig_inputArgs = (union inputArgs *)sig_req->uc_data; - sig_inputArgs->ih.opcode = CODA_SIGNAL; - sig_inputArgs->ih.unique = req->uc_unique; - - sig_req->uc_flags = REQ_ASYNC; - sig_req->uc_opcode = sig_inputArgs->ih.opcode; - sig_req->uc_unique = sig_inputArgs->ih.unique; - sig_req->uc_inSize = sizeof(struct coda_in_hdr); - sig_req->uc_outSize = sizeof(struct coda_in_hdr); - - /* insert at head of queue! */ - list_add(&(sig_req->uc_chain), &vcommp->vc_pending); - wake_up_interruptible(&vcommp->vc_waitq); - } else { - printk("Coda: Strange interruption..\n"); - error = -EINTR; - } - } else { /* If venus died i.e. !VC_OPEN(vcommp) */ - printk("coda_upcall: Venus dead on (op,un) (%d.%d) flags %d\n", - req->uc_opcode, req->uc_unique, req->uc_flags); - error = -ENODEV; } - exit: - upc_free(req); + /* Interrupted before venus read it. */ + if (!(req->uc_flags & REQ_READ)) + goto exit; + + /* Venus saw the upcall, make sure we can send interrupt signal */ + if (!vcp->vc_inuse) { + printk(KERN_INFO "coda: Venus dead, not sending signal.\n"); + goto exit; + } + + error = -ENOMEM; + sig_req = kmalloc(sizeof(struct upc_req), GFP_KERNEL); + if (!sig_req) goto exit; + + CODA_ALLOC((sig_req->uc_data), char *, sizeof(struct coda_in_hdr)); + if (!sig_req->uc_data) { + kfree(sig_req); + goto exit; + } + + error = -EINTR; + sig_inputArgs = (union inputArgs *)sig_req->uc_data; + sig_inputArgs->ih.opcode = CODA_SIGNAL; + sig_inputArgs->ih.unique = req->uc_unique; + + sig_req->uc_flags = REQ_ASYNC; + sig_req->uc_opcode = sig_inputArgs->ih.opcode; + sig_req->uc_unique = sig_inputArgs->ih.unique; + sig_req->uc_inSize = sizeof(struct coda_in_hdr); + sig_req->uc_outSize = sizeof(struct coda_in_hdr); + + /* insert at head of queue! */ + list_add(&(sig_req->uc_chain), &vcp->vc_pending); + wake_up_interruptible(&vcp->vc_waitq); + +exit: + kfree(req); return error; } @@ -838,77 +810,66 @@ static int coda_upcall(struct coda_sb_info *sbi, int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb) { + struct inode *inode = NULL; + struct CodaFid *fid, *newfid; + /* Handle invalidation requests. */ - if ( !sb || !sb->s_root || !sb->s_root->d_inode) - return 0; - - switch (opcode) { - - case CODA_FLUSH : { - coda_cache_clear_all(sb); - shrink_dcache_sb(sb); - coda_flag_inode(sb->s_root->d_inode, C_FLUSH); - return(0); - } - - case CODA_PURGEUSER : { - coda_cache_clear_all(sb); - return(0); - } - - case CODA_ZAPDIR : { - struct inode *inode; - struct CodaFid *fid = &out->coda_zapdir.CodaFid; - - inode = coda_fid_to_inode(fid, sb); - if (inode) { - coda_flag_inode_children(inode, C_PURGE); - coda_flag_inode(inode, C_VATTR); - iput(inode); - } - - return(0); - } - - case CODA_ZAPFILE : { - struct inode *inode; - struct CodaFid *fid = &out->coda_zapfile.CodaFid; - inode = coda_fid_to_inode(fid, sb); - if ( inode ) { - coda_flag_inode(inode, C_VATTR); - iput(inode); - } - return 0; - } - - case CODA_PURGEFID : { - struct inode *inode; - struct CodaFid *fid = &out->coda_purgefid.CodaFid; - inode = coda_fid_to_inode(fid, sb); - if ( inode ) { + if ( !sb || !sb->s_root) + return 0; + + switch (opcode) { + case CODA_FLUSH: + coda_cache_clear_all(sb); + shrink_dcache_sb(sb); + if (sb->s_root->d_inode) + coda_flag_inode(sb->s_root->d_inode, C_FLUSH); + break; + + case CODA_PURGEUSER: + coda_cache_clear_all(sb); + break; + + case CODA_ZAPDIR: + fid = &out->coda_zapdir.CodaFid; + inode = coda_fid_to_inode(fid, sb); + if (inode) { + coda_flag_inode_children(inode, C_PURGE); + coda_flag_inode(inode, C_VATTR); + } + break; + + case CODA_ZAPFILE: + fid = &out->coda_zapfile.CodaFid; + inode = coda_fid_to_inode(fid, sb); + if (inode) + coda_flag_inode(inode, C_VATTR); + break; + + case CODA_PURGEFID: + fid = &out->coda_purgefid.CodaFid; + inode = coda_fid_to_inode(fid, sb); + if (inode) { coda_flag_inode_children(inode, C_PURGE); /* catch the dentries later if some are still busy */ coda_flag_inode(inode, C_PURGE); d_prune_aliases(inode); - iput(inode); - } - return 0; - } - - case CODA_REPLACE : { - struct inode *inode; - struct CodaFid *oldfid = &out->coda_replace.OldFid; - struct CodaFid *newfid = &out->coda_replace.NewFid; - inode = coda_fid_to_inode(oldfid, sb); - if ( inode ) { - coda_replace_fid(inode, oldfid, newfid); - iput(inode); - } - return 0; - } - } - return 0; + } + break; + + case CODA_REPLACE: + fid = &out->coda_replace.OldFid; + newfid = &out->coda_replace.NewFid; + inode = coda_fid_to_inode(fid, sb); + if (inode) + coda_replace_fid(inode, fid, newfid); + break; + } + + if (inode) + iput(inode); + + return 0; } |