nfsd: track last inode only in use_wgather case
[safe/jmp/linux-2.6] / fs / udf / namei.c
index e9f5872..6a29fa3 100644 (file)
@@ -31,6 +31,8 @@
 #include <linux/smp_lock.h>
 #include <linux/buffer_head.h>
 #include <linux/sched.h>
+#include <linux/crc-itu-t.h>
+#include <linux/exportfs.h>
 
 static inline int udf_match(int len1, const char *name1, int len2,
                            const char *name2)
@@ -45,7 +47,7 @@ int udf_write_fi(struct inode *inode, struct fileIdentDesc *cfi,
                 struct fileIdentDesc *sfi, struct udf_fileident_bh *fibh,
                 uint8_t *impuse, uint8_t *fileident)
 {
-       uint16_t crclen = fibh->eoffset - fibh->soffset - sizeof(tag);
+       uint16_t crclen = fibh->eoffset - fibh->soffset - sizeof(struct tag);
        uint16_t crc;
        int offset;
        uint16_t liu = le16_to_cpu(cfi->lengthOfImpUse);
@@ -97,25 +99,23 @@ int udf_write_fi(struct inode *inode, struct fileIdentDesc *cfi,
                memset(fibh->ebh->b_data, 0x00, padlen + offset);
        }
 
-       crc = udf_crc((uint8_t *)cfi + sizeof(tag),
-                     sizeof(struct fileIdentDesc) - sizeof(tag), 0);
+       crc = crc_itu_t(0, (uint8_t *)cfi + sizeof(struct tag),
+                     sizeof(struct fileIdentDesc) - sizeof(struct tag));
 
        if (fibh->sbh == fibh->ebh) {
-               crc = udf_crc((uint8_t *)sfi->impUse,
-                             crclen + sizeof(tag) -
-                             sizeof(struct fileIdentDesc), crc);
+               crc = crc_itu_t(crc, (uint8_t *)sfi->impUse,
+                             crclen + sizeof(struct tag) -
+                             sizeof(struct fileIdentDesc));
        } else if (sizeof(struct fileIdentDesc) >= -fibh->soffset) {
-               crc = udf_crc(fibh->ebh->b_data +
+               crc = crc_itu_t(crc, fibh->ebh->b_data +
                                        sizeof(struct fileIdentDesc) +
                                        fibh->soffset,
-                             crclen + sizeof(tag) -
-                                       sizeof(struct fileIdentDesc),
-                             crc);
+                             crclen + sizeof(struct tag) -
+                                       sizeof(struct fileIdentDesc));
        } else {
-               crc = udf_crc((uint8_t *)sfi->impUse,
-                             -fibh->soffset - sizeof(struct fileIdentDesc),
-                             crc);
-               crc = udf_crc(fibh->ebh->b_data, fibh->eoffset, crc);
+               crc = crc_itu_t(crc, (uint8_t *)sfi->impUse,
+                             -fibh->soffset - sizeof(struct fileIdentDesc));
+               crc = crc_itu_t(crc, fibh->ebh->b_data, fibh->eoffset);
        }
 
        cfi->descTag.descCRC = cpu_to_le16(crc);
@@ -142,62 +142,58 @@ int udf_write_fi(struct inode *inode, struct fileIdentDesc *cfi,
 }
 
 static struct fileIdentDesc *udf_find_entry(struct inode *dir,
-                                           struct dentry *dentry,
+                                           struct qstr *child,
                                            struct udf_fileident_bh *fibh,
                                            struct fileIdentDesc *cfi)
 {
        struct fileIdentDesc *fi = NULL;
        loff_t f_pos;
        int block, flen;
-       char fname[UDF_NAME_LEN];
+       char *fname = NULL;
        char *nameptr;
        uint8_t lfi;
        uint16_t liu;
        loff_t size;
-       kernel_lb_addr eloc;
+       struct kernel_lb_addr eloc;
        uint32_t elen;
        sector_t offset;
        struct extent_position epos = {};
        struct udf_inode_info *dinfo = UDF_I(dir);
+       int isdotdot = child->len == 2 &&
+               child->name[0] == '.' && child->name[1] == '.';
 
        size = udf_ext0_offset(dir) + dir->i_size;
        f_pos = udf_ext0_offset(dir);
 
+       fibh->sbh = fibh->ebh = NULL;
        fibh->soffset = fibh->eoffset = f_pos & (dir->i_sb->s_blocksize - 1);
-       if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
-               fibh->sbh = fibh->ebh = NULL;
-       else if (inode_bmap(dir, f_pos >> dir->i_sb->s_blocksize_bits,
-                             &epos, &eloc, &elen, &offset) ==
-                                       (EXT_RECORDED_ALLOCATED >> 30)) {
-               block = udf_get_lb_pblock(dir->i_sb, eloc, offset);
+       if (dinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
+               if (inode_bmap(dir, f_pos >> dir->i_sb->s_blocksize_bits, &epos,
+                   &eloc, &elen, &offset) != (EXT_RECORDED_ALLOCATED >> 30))
+                       goto out_err;
+               block = udf_get_lb_pblock(dir->i_sb, &eloc, offset);
                if ((++offset << dir->i_sb->s_blocksize_bits) < elen) {
                        if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
-                               epos.offset -= sizeof(short_ad);
+                               epos.offset -= sizeof(struct short_ad);
                        else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
-                               epos.offset -= sizeof(long_ad);
+                               epos.offset -= sizeof(struct long_ad);
                } else
                        offset = 0;
 
                fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block);
-               if (!fibh->sbh) {
-                       brelse(epos.bh);
-                       return NULL;
-               }
-       } else {
-               brelse(epos.bh);
-               return NULL;
+               if (!fibh->sbh)
+                       goto out_err;
        }
 
+       fname = kmalloc(UDF_NAME_LEN, GFP_NOFS);
+       if (!fname)
+               goto out_err;
+
        while (f_pos < size) {
                fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &epos, &eloc,
                                        &elen, &offset);
-               if (!fi) {
-                       if (fibh->sbh != fibh->ebh)
-                               brelse(fibh->ebh);
-                       brelse(fibh->sbh);
-                       brelse(epos.bh);
-                       return NULL;
-               }
+               if (!fi)
+                       goto out_err;
 
                liu = le16_to_cpu(cfi->lengthOfImpUse);
                lfi = cfi->lengthFileIdent;
@@ -232,23 +228,30 @@ static struct fileIdentDesc *udf_find_entry(struct inode *dir,
                                continue;
                }
 
+               if ((cfi->fileCharacteristics & FID_FILE_CHAR_PARENT) &&
+                   isdotdot) {
+                       brelse(epos.bh);
+                       return fi;
+               }
+
                if (!lfi)
                        continue;
 
                flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi);
-               if (flen && udf_match(flen, fname, dentry->d_name.len,
-                                     dentry->d_name.name)) {
-                       brelse(epos.bh);
-                       return fi;
-               }
+               if (flen && udf_match(flen, fname, child->len, child->name))
+                       goto out_ok;
        }
 
+out_err:
+       fi = NULL;
        if (fibh->sbh != fibh->ebh)
                brelse(fibh->ebh);
        brelse(fibh->sbh);
+out_ok:
        brelse(epos.bh);
+       kfree(fname);
 
-       return NULL;
+       return fi;
 }
 
 static struct dentry *udf_lookup(struct inode *dir, struct dentry *dentry,
@@ -265,7 +268,7 @@ static struct dentry *udf_lookup(struct inode *dir, struct dentry *dentry,
 #ifdef UDF_RECOVERY
        /* temporary shorthand for specifying files by inode number */
        if (!strncmp(dentry->d_name.name, ".B=", 3)) {
-               kernel_lb_addr lb = {
+               struct kernel_lb_addr lb = {
                        .logicalBlockNum = 0,
                        .partitionReferenceNum =
                                simple_strtoul(dentry->d_name.name + 3,
@@ -279,21 +282,23 @@ static struct dentry *udf_lookup(struct inode *dir, struct dentry *dentry,
        } else
 #endif /* UDF_RECOVERY */
 
-       if (udf_find_entry(dir, dentry, &fibh, &cfi)) {
+       if (udf_find_entry(dir, &dentry->d_name, &fibh, &cfi)) {
+               struct kernel_lb_addr loc;
+
                if (fibh.sbh != fibh.ebh)
                        brelse(fibh.ebh);
                brelse(fibh.sbh);
 
-               inode = udf_iget(dir->i_sb, lelb_to_cpu(cfi.icb.extLocation));
+               loc = lelb_to_cpu(cfi.icb.extLocation);
+               inode = udf_iget(dir->i_sb, &loc);
                if (!inode) {
                        unlock_kernel();
                        return ERR_PTR(-EACCES);
                }
        }
        unlock_kernel();
-       d_add(dentry, inode);
 
-       return NULL;
+       return d_splice_alias(inode, dentry);
 }
 
 static struct fileIdentDesc *udf_add_entry(struct inode *dir,
@@ -303,7 +308,7 @@ static struct fileIdentDesc *udf_add_entry(struct inode *dir,
 {
        struct super_block *sb = dir->i_sb;
        struct fileIdentDesc *fi = NULL;
-       char name[UDF_NAME_LEN];
+       char *name = NULL;
        int namelen;
        loff_t f_pos;
        loff_t size = udf_ext0_offset(dir) + dir->i_size;
@@ -311,22 +316,29 @@ static struct fileIdentDesc *udf_add_entry(struct inode *dir,
        uint8_t lfi;
        uint16_t liu;
        int block;
-       kernel_lb_addr eloc;
-       uint32_t elen;
+       struct kernel_lb_addr eloc;
+       uint32_t elen = 0;
        sector_t offset;
        struct extent_position epos = {};
        struct udf_inode_info *dinfo;
 
+       fibh->sbh = fibh->ebh = NULL;
+       name = kmalloc(UDF_NAME_LEN, GFP_NOFS);
+       if (!name) {
+               *err = -ENOMEM;
+               goto out_err;
+       }
+
        if (dentry) {
                if (!dentry->d_name.len) {
                        *err = -EINVAL;
-                       return NULL;
+                       goto out_err;
                }
                namelen = udf_put_filename(sb, dentry->d_name.name, name,
                                                 dentry->d_name.len);
                if (!namelen) {
                        *err = -ENAMETOOLONG;
-                       return NULL;
+                       goto out_err;
                }
        } else {
                namelen = 0;
@@ -338,33 +350,30 @@ static struct fileIdentDesc *udf_add_entry(struct inode *dir,
 
        fibh->soffset = fibh->eoffset = f_pos & (dir->i_sb->s_blocksize - 1);
        dinfo = UDF_I(dir);
-       if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
-               fibh->sbh = fibh->ebh = NULL;
-       else if (inode_bmap(dir, f_pos >> dir->i_sb->s_blocksize_bits,
-                             &epos, &eloc, &elen, &offset) ==
-                                       (EXT_RECORDED_ALLOCATED >> 30)) {
-               block = udf_get_lb_pblock(dir->i_sb, eloc, offset);
+       if (dinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
+               if (inode_bmap(dir, f_pos >> dir->i_sb->s_blocksize_bits, &epos,
+                   &eloc, &elen, &offset) != (EXT_RECORDED_ALLOCATED >> 30)) {
+                       block = udf_get_lb_pblock(dir->i_sb,
+                                       &dinfo->i_location, 0);
+                       fibh->soffset = fibh->eoffset = sb->s_blocksize;
+                       goto add;
+               }
+               block = udf_get_lb_pblock(dir->i_sb, &eloc, offset);
                if ((++offset << dir->i_sb->s_blocksize_bits) < elen) {
                        if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
-                               epos.offset -= sizeof(short_ad);
+                               epos.offset -= sizeof(struct short_ad);
                        else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
-                               epos.offset -= sizeof(long_ad);
+                               epos.offset -= sizeof(struct long_ad);
                } else
                        offset = 0;
 
                fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block);
                if (!fibh->sbh) {
-                       brelse(epos.bh);
                        *err = -EIO;
-                       return NULL;
+                       goto out_err;
                }
 
                block = dinfo->i_location.logicalBlockNum;
-       } else {
-               block = udf_get_lb_pblock(dir->i_sb, dinfo->i_location, 0);
-               fibh->sbh = fibh->ebh = NULL;
-               fibh->soffset = fibh->eoffset = sb->s_blocksize;
-               goto add;
        }
 
        while (f_pos < size) {
@@ -372,12 +381,8 @@ static struct fileIdentDesc *udf_add_entry(struct inode *dir,
                                        &elen, &offset);
 
                if (!fi) {
-                       if (fibh->sbh != fibh->ebh)
-                               brelse(fibh->ebh);
-                       brelse(fibh->sbh);
-                       brelse(epos.bh);
                        *err = -EIO;
-                       return NULL;
+                       goto out_err;
                }
 
                liu = le16_to_cpu(cfi->lengthOfImpUse);
@@ -386,7 +391,6 @@ static struct fileIdentDesc *udf_add_entry(struct inode *dir,
                if ((cfi->fileCharacteristics & FID_FILE_CHAR_DELETED) != 0) {
                        if (((sizeof(struct fileIdentDesc) +
                                        liu + lfi + 3) & ~3) == nfidlen) {
-                               brelse(epos.bh);
                                cfi->descTag.tagSerialNum = cpu_to_le16(1);
                                cfi->fileVersionNum = cpu_to_le16(1);
                                cfi->fileCharacteristics = 0;
@@ -394,23 +398,24 @@ static struct fileIdentDesc *udf_add_entry(struct inode *dir,
                                cfi->lengthOfImpUse = cpu_to_le16(0);
                                if (!udf_write_fi(dir, cfi, fi, fibh, NULL,
                                                  name))
-                                       return fi;
+                                       goto out_ok;
                                else {
                                        *err = -EIO;
-                                       return NULL;
+                                       goto out_err;
                                }
                        }
                }
        }
 
 add:
-       if (dinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
+       /* Is there any extent whose size we need to round up? */
+       if (dinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB && elen) {
                elen = (elen + sb->s_blocksize - 1) & ~(sb->s_blocksize - 1);
                if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
-                       epos.offset -= sizeof(short_ad);
+                       epos.offset -= sizeof(struct short_ad);
                else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
-                       epos.offset -= sizeof(long_ad);
-               udf_write_aext(dir, &epos, eloc, elen, 1);
+                       epos.offset -= sizeof(struct long_ad);
+               udf_write_aext(dir, &epos, &eloc, elen, 1);
        }
        f_pos += nfidlen;
 
@@ -427,7 +432,7 @@ add:
                fibh->sbh = fibh->ebh =
                                udf_expand_dir_adinicb(dir, &block, err);
                if (!fibh->sbh)
-                       return NULL;
+                       goto out_err;
                epos.block = dinfo->i_location;
                epos.offset = udf_file_entry_alloc_offset(dir);
                /* Load extent udf_expand_dir_adinicb() has created */
@@ -468,11 +473,8 @@ add:
                                                dir->i_sb->s_blocksize_bits);
                fibh->ebh = udf_bread(dir,
                                f_pos >> dir->i_sb->s_blocksize_bits, 1, err);
-               if (!fibh->ebh) {
-                       brelse(epos.bh);
-                       brelse(fibh->sbh);
-                       return NULL;
-               }
+               if (!fibh->ebh)
+                       goto out_err;
 
                if (!fibh->soffset) {
                        if (udf_next_aext(dir, &epos, &eloc, &elen, 1) ==
@@ -495,28 +497,33 @@ add:
        memset(cfi, 0, sizeof(struct fileIdentDesc));
        if (UDF_SB(sb)->s_udfrev >= 0x0200)
                udf_new_tag((char *)cfi, TAG_IDENT_FID, 3, 1, block,
-                           sizeof(tag));
+                           sizeof(struct tag));
        else
                udf_new_tag((char *)cfi, TAG_IDENT_FID, 2, 1, block,
-                           sizeof(tag));
+                           sizeof(struct tag));
        cfi->fileVersionNum = cpu_to_le16(1);
        cfi->lengthFileIdent = namelen;
        cfi->lengthOfImpUse = cpu_to_le16(0);
        if (!udf_write_fi(dir, cfi, fi, fibh, NULL, name)) {
-               brelse(epos.bh);
                dir->i_size += nfidlen;
                if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
                        dinfo->i_lenAlloc += nfidlen;
                mark_inode_dirty(dir);
-               return fi;
+               goto out_ok;
        } else {
-               brelse(epos.bh);
-               if (fibh->sbh != fibh->ebh)
-                       brelse(fibh->ebh);
-               brelse(fibh->sbh);
                *err = -EIO;
-               return NULL;
+               goto out_err;
        }
+
+out_err:
+       fi = NULL;
+       if (fibh->sbh != fibh->ebh)
+               brelse(fibh->ebh);
+       brelse(fibh->sbh);
+out_ok:
+       brelse(epos.bh);
+       kfree(name);
+       return fi;
 }
 
 static int udf_delete_entry(struct inode *inode, struct fileIdentDesc *fi,
@@ -526,7 +533,7 @@ static int udf_delete_entry(struct inode *inode, struct fileIdentDesc *fi,
        cfi->fileCharacteristics |= FID_FILE_CHAR_DELETED;
 
        if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT))
-               memset(&(cfi->icb), 0x00, sizeof(long_ad));
+               memset(&(cfi->icb), 0x00, sizeof(struct long_ad));
 
        return udf_write_fi(inode, cfi, fi, fibh, NULL, NULL);
 }
@@ -600,7 +607,7 @@ static int udf_mknod(struct inode *dir, struct dentry *dentry, int mode,
                goto out;
 
        iinfo = UDF_I(inode);
-       inode->i_uid = current->fsuid;
+       inode->i_uid = current_fsuid();
        init_special_inode(inode, mode, rdev);
        fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err);
        if (!fi) {
@@ -706,7 +713,7 @@ static int empty_dir(struct inode *dir)
        loff_t f_pos;
        loff_t size = udf_ext0_offset(dir) + dir->i_size;
        int block;
-       kernel_lb_addr eloc;
+       struct kernel_lb_addr eloc;
        uint32_t elen;
        sector_t offset;
        struct extent_position epos = {};
@@ -720,12 +727,12 @@ static int empty_dir(struct inode *dir)
        else if (inode_bmap(dir, f_pos >> dir->i_sb->s_blocksize_bits,
                              &epos, &eloc, &elen, &offset) ==
                                        (EXT_RECORDED_ALLOCATED >> 30)) {
-               block = udf_get_lb_pblock(dir->i_sb, eloc, offset);
+               block = udf_get_lb_pblock(dir->i_sb, &eloc, offset);
                if ((++offset << dir->i_sb->s_blocksize_bits) < elen) {
                        if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
-                               epos.offset -= sizeof(short_ad);
+                               epos.offset -= sizeof(struct short_ad);
                        else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
-                               epos.offset -= sizeof(long_ad);
+                               epos.offset -= sizeof(struct long_ad);
                } else
                        offset = 0;
 
@@ -774,17 +781,17 @@ static int udf_rmdir(struct inode *dir, struct dentry *dentry)
        struct inode *inode = dentry->d_inode;
        struct udf_fileident_bh fibh;
        struct fileIdentDesc *fi, cfi;
-       kernel_lb_addr tloc;
+       struct kernel_lb_addr tloc;
 
        retval = -ENOENT;
        lock_kernel();
-       fi = udf_find_entry(dir, dentry, &fibh, &cfi);
+       fi = udf_find_entry(dir, &dentry->d_name, &fibh, &cfi);
        if (!fi)
                goto out;
 
        retval = -EIO;
        tloc = lelb_to_cpu(cfi.icb.extLocation);
-       if (udf_get_lb_pblock(dir->i_sb, tloc, 0) != inode->i_ino)
+       if (udf_get_lb_pblock(dir->i_sb, &tloc, 0) != inode->i_ino)
                goto end_rmdir;
        retval = -ENOTEMPTY;
        if (!empty_dir(inode))
@@ -820,17 +827,17 @@ static int udf_unlink(struct inode *dir, struct dentry *dentry)
        struct udf_fileident_bh fibh;
        struct fileIdentDesc *fi;
        struct fileIdentDesc cfi;
-       kernel_lb_addr tloc;
+       struct kernel_lb_addr tloc;
 
        retval = -ENOENT;
        lock_kernel();
-       fi = udf_find_entry(dir, dentry, &fibh, &cfi);
+       fi = udf_find_entry(dir, &dentry->d_name, &fibh, &cfi);
        if (!fi)
                goto out;
 
        retval = -EIO;
        tloc = lelb_to_cpu(cfi.icb.extLocation);
-       if (udf_get_lb_pblock(dir->i_sb, tloc, 0) != inode->i_ino)
+       if (udf_get_lb_pblock(dir->i_sb, &tloc, 0) != inode->i_ino)
                goto end_unlink;
 
        if (!inode->i_nlink) {
@@ -871,7 +878,7 @@ static int udf_symlink(struct inode *dir, struct dentry *dentry,
        char *ea;
        int err;
        int block;
-       char name[UDF_NAME_LEN];
+       char *name = NULL;
        int namelen;
        struct buffer_head *bh;
        struct udf_inode_info *iinfo;
@@ -881,14 +888,20 @@ static int udf_symlink(struct inode *dir, struct dentry *dentry,
        if (!inode)
                goto out;
 
+       name = kmalloc(UDF_NAME_LEN, GFP_NOFS);
+       if (!name) {
+               err = -ENOMEM;
+               goto out_no_entry;
+       }
+
        iinfo = UDF_I(inode);
        inode->i_mode = S_IFLNK | S_IRWXUGO;
        inode->i_data.a_ops = &udf_symlink_aops;
        inode->i_op = &page_symlink_inode_operations;
 
        if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
-               kernel_lb_addr eloc;
-               uint32_t elen;
+               struct kernel_lb_addr eloc;
+               uint32_t bsize;
 
                block = udf_new_block(inode->i_sb, inode,
                                iinfo->i_location.partitionReferenceNum,
@@ -901,9 +914,9 @@ static int udf_symlink(struct inode *dir, struct dentry *dentry,
                eloc.logicalBlockNum = block;
                eloc.partitionReferenceNum =
                                iinfo->i_location.partitionReferenceNum;
-               elen = inode->i_sb->s_blocksize;
-               iinfo->i_lenExtents = elen;
-               udf_add_aext(inode, &epos, eloc, elen, 0);
+               bsize = inode->i_sb->s_blocksize;
+               iinfo->i_lenExtents = bsize;
+               udf_add_aext(inode, &epos, &eloc, bsize, 0);
                brelse(epos.bh);
 
                block = udf_get_pblock(inode->i_sb, block,
@@ -1020,6 +1033,7 @@ static int udf_symlink(struct inode *dir, struct dentry *dentry,
        err = 0;
 
 out:
+       kfree(name);
        unlock_kernel();
        return err;
 
@@ -1097,22 +1111,22 @@ static int udf_rename(struct inode *old_dir, struct dentry *old_dentry,
        struct fileIdentDesc ocfi, ncfi;
        struct buffer_head *dir_bh = NULL;
        int retval = -ENOENT;
-       kernel_lb_addr tloc;
+       struct kernel_lb_addr tloc;
        struct udf_inode_info *old_iinfo = UDF_I(old_inode);
 
        lock_kernel();
-       ofi = udf_find_entry(old_dir, old_dentry, &ofibh, &ocfi);
+       ofi = udf_find_entry(old_dir, &old_dentry->d_name, &ofibh, &ocfi);
        if (ofi) {
                if (ofibh.sbh != ofibh.ebh)
                        brelse(ofibh.ebh);
                brelse(ofibh.sbh);
        }
        tloc = lelb_to_cpu(ocfi.icb.extLocation);
-       if (!ofi || udf_get_lb_pblock(old_dir->i_sb, tloc, 0)
+       if (!ofi || udf_get_lb_pblock(old_dir->i_sb, &tloc, 0)
            != old_inode->i_ino)
                goto end_rename;
 
-       nfi = udf_find_entry(new_dir, new_dentry, &nfibh, &ncfi);
+       nfi = udf_find_entry(new_dir, &new_dentry->d_name, &nfibh, &ncfi);
        if (nfi) {
                if (!new_inode) {
                        if (nfibh.sbh != nfibh.ebh)
@@ -1147,7 +1161,7 @@ static int udf_rename(struct inode *old_dir, struct dentry *old_dentry,
                if (!dir_fi)
                        goto end_rename;
                tloc = lelb_to_cpu(dir_fi->icb.extLocation);
-               if (udf_get_lb_pblock(old_inode->i_sb, tloc, 0) !=
+               if (udf_get_lb_pblock(old_inode->i_sb, &tloc, 0) !=
                                old_dir->i_ino)
                        goto end_rename;
 
@@ -1176,11 +1190,11 @@ static int udf_rename(struct inode *old_dir, struct dentry *old_dentry,
         */
        ncfi.fileVersionNum = ocfi.fileVersionNum;
        ncfi.fileCharacteristics = ocfi.fileCharacteristics;
-       memcpy(&(ncfi.icb), &(ocfi.icb), sizeof(long_ad));
+       memcpy(&(ncfi.icb), &(ocfi.icb), sizeof(struct long_ad));
        udf_write_fi(new_dir, &ncfi, nfi, &nfibh, NULL, NULL);
 
        /* The old fid may have moved - find it again */
-       ofi = udf_find_entry(old_dir, old_dentry, &ofibh, &ocfi);
+       ofi = udf_find_entry(old_dir, &old_dentry->d_name, &ofibh, &ocfi);
        udf_delete_entry(old_dir, ofi, &ofibh, &ocfi);
 
        if (new_inode) {
@@ -1229,6 +1243,119 @@ end_rename:
        return retval;
 }
 
+static struct dentry *udf_get_parent(struct dentry *child)
+{
+       struct kernel_lb_addr tloc;
+       struct inode *inode = NULL;
+       struct qstr dotdot = {.name = "..", .len = 2};
+       struct fileIdentDesc cfi;
+       struct udf_fileident_bh fibh;
+
+       lock_kernel();
+       if (!udf_find_entry(child->d_inode, &dotdot, &fibh, &cfi))
+               goto out_unlock;
+
+       if (fibh.sbh != fibh.ebh)
+               brelse(fibh.ebh);
+       brelse(fibh.sbh);
+
+       tloc = lelb_to_cpu(cfi.icb.extLocation);
+       inode = udf_iget(child->d_inode->i_sb, &tloc);
+       if (!inode)
+               goto out_unlock;
+       unlock_kernel();
+
+       return d_obtain_alias(inode);
+out_unlock:
+       unlock_kernel();
+       return ERR_PTR(-EACCES);
+}
+
+
+static struct dentry *udf_nfs_get_inode(struct super_block *sb, u32 block,
+                                       u16 partref, __u32 generation)
+{
+       struct inode *inode;
+       struct kernel_lb_addr loc;
+
+       if (block == 0)
+               return ERR_PTR(-ESTALE);
+
+       loc.logicalBlockNum = block;
+       loc.partitionReferenceNum = partref;
+       inode = udf_iget(sb, &loc);
+
+       if (inode == NULL)
+               return ERR_PTR(-ENOMEM);
+
+       if (generation && inode->i_generation != generation) {
+               iput(inode);
+               return ERR_PTR(-ESTALE);
+       }
+       return d_obtain_alias(inode);
+}
+
+static struct dentry *udf_fh_to_dentry(struct super_block *sb,
+                                      struct fid *fid, int fh_len, int fh_type)
+{
+       if ((fh_len != 3 && fh_len != 5) ||
+           (fh_type != FILEID_UDF_WITH_PARENT &&
+            fh_type != FILEID_UDF_WITHOUT_PARENT))
+               return NULL;
+
+       return udf_nfs_get_inode(sb, fid->udf.block, fid->udf.partref,
+                       fid->udf.generation);
+}
+
+static struct dentry *udf_fh_to_parent(struct super_block *sb,
+                                      struct fid *fid, int fh_len, int fh_type)
+{
+       if (fh_len != 5 || fh_type != FILEID_UDF_WITH_PARENT)
+               return NULL;
+
+       return udf_nfs_get_inode(sb, fid->udf.parent_block,
+                                fid->udf.parent_partref,
+                                fid->udf.parent_generation);
+}
+static int udf_encode_fh(struct dentry *de, __u32 *fh, int *lenp,
+                        int connectable)
+{
+       int len = *lenp;
+       struct inode *inode =  de->d_inode;
+       struct kernel_lb_addr location = UDF_I(inode)->i_location;
+       struct fid *fid = (struct fid *)fh;
+       int type = FILEID_UDF_WITHOUT_PARENT;
+
+       if (len < 3 || (connectable && len < 5))
+               return 255;
+
+       *lenp = 3;
+       fid->udf.block = location.logicalBlockNum;
+       fid->udf.partref = location.partitionReferenceNum;
+       fid->udf.generation = inode->i_generation;
+
+       if (connectable && !S_ISDIR(inode->i_mode)) {
+               spin_lock(&de->d_lock);
+               inode = de->d_parent->d_inode;
+               location = UDF_I(inode)->i_location;
+               fid->udf.parent_block = location.logicalBlockNum;
+               fid->udf.parent_partref = location.partitionReferenceNum;
+               fid->udf.parent_generation = inode->i_generation;
+               spin_unlock(&de->d_lock);
+               *lenp = 5;
+               type = FILEID_UDF_WITH_PARENT;
+       }
+
+       return type;
+}
+
+const struct export_operations udf_export_ops = {
+       .encode_fh      = udf_encode_fh,
+       .fh_to_dentry   = udf_fh_to_dentry,
+       .fh_to_parent   = udf_fh_to_parent,
+       .get_parent     = udf_get_parent,
+};
+
 const struct inode_operations udf_dir_inode_operations = {
        .lookup                         = udf_lookup,
        .create                         = udf_create,