--- linux2/include/linux/fs.h Thu Jun 15 18:19:50 2000 +++ linux/include/linux/fs.h Thu Jun 15 18:24:24 2000 @@ -762,6 +762,12 @@ int (*revalidate) (struct dentry *); int (*setattr) (struct dentry *, struct iattr *); int (*getattr) (struct dentry *, struct iattr *); + +}; + +struct fhandle { + unsigned long id; + unsigned long version; }; /* @@ -779,6 +785,8 @@ int (*remount_fs) (struct super_block *, int *, char *); void (*clear_inode) (struct inode *); void (*umount_begin) (struct super_block *); + int (*fhandle_to_dentry) (struct super_block *, struct fhandle *, struct dentry **); + int (*dentry_to_fhandle) (struct dentry *, struct fhandle *); }; struct dquot_operations { --- linux2/fs/nfsd/export.c Thu Jun 15 18:19:50 2000 +++ linux/fs/nfsd/export.c Thu Jun 15 18:25:49 2000 @@ -214,7 +214,8 @@ err = -EINVAL; if (!(inode->i_sb->s_type->fs_flags & FS_REQUIRES_DEV) || - inode->i_sb->s_op->read_inode == NULL) { + inode->i_sb->s_op->fhandle_to_dentry == NULL || + inode->i_sb->s_op->dentry_to_fhandle == NULL) { dprintk("exp_export: export of invalid fs type.\n"); goto finish; } --- linux2/fs/nfsd/nfsfh.c Thu Jun 15 18:19:50 2000 +++ linux/fs/nfsd/nfsfh.c Thu Jun 15 18:31:54 2000 @@ -29,452 +29,6 @@ static int nfsd_nr_verified = 0; static int nfsd_nr_put = 0; - -struct nfsd_getdents_callback { - struct qstr *name; /* name that was found. name->name already points to a buffer */ - unsigned long ino; /* the inum we are looking for */ - int found; /* inode matched? */ - int sequence; /* sequence counter */ -}; - -/* - * A rather strange filldir function to capture - * the name matching the specified inode number. - */ -static int filldir_one(void * __buf, const char * name, int len, - off_t pos, ino_t ino) -{ - struct nfsd_getdents_callback *buf = __buf; - struct qstr *qs = buf->name; - char *nbuf = (char*)qs->name; /* cast is to get rid of "const" */ - int result = 0; - - buf->sequence++; -#ifdef NFSD_DEBUG_VERBOSE -dprintk("filldir_one: seq=%d, ino=%ld, name=%s\n", buf->sequence, ino, name); -#endif - if (buf->ino == ino) { - qs->len = len; - memcpy(nbuf, name, len); - nbuf[len] = '\0'; - buf->found = 1; - result = -1; - } - return result; -} - -/* - * Read a directory and return the name of the specified entry. - * i_sem is already down(). - * The whole thing is a total BS. It should not be done via readdir(), damnit! - * Oh, well, as soon as it will be in filesystems... - */ -static int get_ino_name(struct dentry *dentry, struct qstr *name, unsigned long ino) -{ - struct inode *dir = dentry->d_inode; - int error; - struct file file; - struct nfsd_getdents_callback buffer; - - error = -ENOTDIR; - if (!dir || !S_ISDIR(dir->i_mode)) - goto out; - error = -EINVAL; - if (!dir->i_fop) - goto out; - /* - * Open the directory ... - */ - error = init_private_file(&file, dentry, FMODE_READ); - if (error) - goto out; - error = -EINVAL; - if (!file.f_op->readdir) - goto out_close; - - buffer.name = name; - buffer.ino = ino; - buffer.found = 0; - buffer.sequence = 0; - while (1) { - int old_seq = buffer.sequence; - error = file.f_op->readdir(&file, &buffer, filldir_one); - if (error < 0) - break; - - error = 0; - if (buffer.found) - break; - error = -ENOENT; - if (old_seq == buffer.sequence) - break; - } - -out_close: - if (file.f_op->release) - file.f_op->release(dir, &file); -out: - return error; -} - -/* this should be provided by each filesystem in an nfsd_operations interface as - * iget isn't really the right interface - */ -static struct dentry *nfsd_iget(struct super_block *sb, unsigned long ino, __u32 generation) -{ - - /* iget isn't really right if the inode is currently unallocated!! - * This should really all be done inside each filesystem - * - * ext2fs' read_inode has been strengthed to return a bad_inode if the inode - * had been deleted. - * - * Currently we don't know the generation for parent directory, so a generation - * of 0 means "accept any" - */ - struct inode *inode; - struct list_head *lp; - struct dentry *result; - inode = iget(sb, ino); - if (is_bad_inode(inode) - || (generation && inode->i_generation != generation) - ) { - /* we didn't find the right inode.. */ - dprintk("fh_verify: Inode %lu, Bad count: %d %d or version %u %u\n", - inode->i_ino, - inode->i_nlink, atomic_read(&inode->i_count), - inode->i_generation, - generation); - - iput(inode); - return ERR_PTR(-ESTALE); - } - /* now to find a dentry. - * If possible, get a well-connected one - */ - for (lp = inode->i_dentry.next; lp != &inode->i_dentry ; lp=lp->next) { - result = list_entry(lp,struct dentry, d_alias); - if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) { - dget(result); - iput(inode); - return result; - } - } - result = d_alloc_root(inode); - if (result == NULL) { - iput(inode); - return ERR_PTR(-ENOMEM); - } - result->d_flags |= DCACHE_NFSD_DISCONNECTED; - d_rehash(result); /* so a dput won't loose it */ - return result; -} - -/* this routine links an IS_ROOT dentry into the dcache tree. It gains "parent" - * as a parent and "name" as a name - * It should possibly go in dcache.c - */ -int d_splice(struct dentry *target, struct dentry *parent, struct qstr *name) -{ - struct dentry *tdentry; -#ifdef NFSD_PARANOIA - if (!IS_ROOT(target)) - printk("nfsd: d_splice with no-root target: %s/%s\n", parent->d_name.name, name->name); - if (!(target->d_flags & DCACHE_NFSD_DISCONNECTED)) - printk("nfsd: d_splice with non-DISCONNECTED target: %s/%s\n", parent->d_name.name, name->name); -#endif - name->hash = full_name_hash(name->name, name->len); - tdentry = d_alloc(parent, name); - if (tdentry == NULL) - return -ENOMEM; - d_move(target, tdentry); - - /* tdentry will have been made a "child" of target (the parent of target) - * make it an IS_ROOT instead - */ - list_del(&tdentry->d_child); - tdentry->d_parent = tdentry; - d_rehash(target); - dput(tdentry); - - /* if parent is properly connected, then we can assert that - * the children are connected, but it must be a singluar (non-forking) - * branch - */ - if (!(parent->d_flags & DCACHE_NFSD_DISCONNECTED)) { - while (target) { - target->d_flags &= ~DCACHE_NFSD_DISCONNECTED; - parent = target; - if (list_empty(&parent->d_subdirs)) - target = NULL; - else { - target = list_entry(parent->d_subdirs.next, struct dentry, d_child); -#ifdef NFSD_PARANOIA - /* must be only child */ - if (target->d_child.next != &parent->d_subdirs - || target->d_child.prev != &parent->d_subdirs) - printk("nfsd: d_splice found non-singular disconnected branch: %s/%s\n", - parent->d_name.name, target->d_name.name); -#endif - } - } - } - return 0; -} - -/* this routine finds the dentry of the parent of a given directory - * it should be in the filesystem accessed by nfsd_operations - * it assumes lookup("..") works. - */ -struct dentry *nfsd_findparent(struct dentry *child) -{ - struct dentry *tdentry, *pdentry; - tdentry = d_alloc(child, &(const struct qstr) {"..", 2, 0}); - if (!tdentry) - return ERR_PTR(-ENOMEM); - - /* I'm going to assume that if the returned dentry is different, then - * it is well connected. But nobody returns different dentrys do they? - */ - pdentry = child->d_inode->i_op->lookup(child->d_inode, tdentry); - d_drop(tdentry); /* we never want ".." hashed */ - if (!pdentry) { - /* I don't want to return a ".." dentry. - * I would prefer to return an unconnected "IS_ROOT" dentry, - * though a properly connected dentry is even better - */ - /* if first or last of alias list is not tdentry, use that - * else make a root dentry - */ - struct list_head *aliases = &tdentry->d_inode->i_dentry; - if (aliases->next != aliases) { - pdentry = list_entry(aliases->next, struct dentry, d_alias); - if (pdentry == tdentry) - pdentry = list_entry(aliases->prev, struct dentry, d_alias); - if (pdentry == tdentry) - pdentry = NULL; - if (pdentry) dget(pdentry); - } - if (pdentry == NULL) { - pdentry = d_alloc_root(igrab(tdentry->d_inode)); - if (pdentry) { - pdentry->d_flags |= DCACHE_NFSD_DISCONNECTED; - d_rehash(pdentry); - } - } - if (pdentry == NULL) - pdentry = ERR_PTR(-ENOMEM); - } - dput(tdentry); /* it is not hashed, it will be discarded */ - return pdentry; -} - -static struct dentry *splice(struct dentry *child, struct dentry *parent) -{ - int err = 0; - struct qstr qs; - char namebuf[256]; - struct list_head *lp; - struct dentry *tmp; - /* child is an IS_ROOT (anonymous) dentry, but it is hypothesised that - * it should be a child of parent. - * We see if we can find a name and, if we can - splice it in. - * We hold the i_sem on the parent the whole time to try to follow locking protocols. - */ - qs.name = namebuf; - down(&parent->d_inode->i_sem); - - /* Now, things might have changed while we waited. - * Possibly a friendly filesystem found child and spliced it in in response - * to a lookup (though nobody does this yet). In this case, just succeed. - */ - if (child->d_parent == parent) goto out; - /* Possibly a new dentry has been made for this child->d_inode in parent by - * a lookup. In this case return that dentry. caller must notice and act accordingly - */ - for (lp = child->d_inode->i_dentry.next; lp != &child->d_inode->i_dentry ; lp=lp->next) { - tmp = list_entry(lp,struct dentry, d_alias); - if (tmp->d_parent == parent) { - child = dget(tmp); - goto out; - } - } - /* well, if we can find a name for child in parent, it should be safe to splice it in */ - err = get_ino_name(parent, &qs, child->d_inode->i_ino); - if (err) - goto out; - tmp = d_lookup(parent, &qs); - if (tmp) { - /* Now that IS odd. I wonder what it means... */ - err = -EEXIST; - printk("nfsd-fh: found a name that I didn't expect: %s/%s\n", parent->d_name.name, qs.name); - dput(tmp); - goto out; - } - err = d_splice(child, parent, &qs); - dprintk("nfsd_fh: found name %s for ino %ld\n", child->d_name.name, child->d_inode->i_ino); - out: - up(&parent->d_inode->i_sem); - if (err) - return ERR_PTR(err); - else - return child; -} - -/* - * This is the basic lookup mechanism for turning an NFS file handle - * into a dentry. - * We use nfsd_iget and if that doesn't return a suitably connected dentry, - * we try to find the parent, and the parent of that and so-on until a - * connection if made. - */ -static struct dentry * -find_fh_dentry(struct super_block *sb, ino_t ino, int generation, ino_t dirino, int needpath) -{ - struct dentry *dentry, *result = NULL; - struct dentry *tmp; - int found =0; - int err; - /* the sb->s_nfsd_free_path_sem semaphore is needed to make sure that only one unconnected (free) - * dcache path ever exists, as otherwise two partial paths might get - * joined together, which would be very confusing. - * If there is ever an unconnected non-root directory, then this lock - * must be held. - */ - - - nfsdstats.fh_lookup++; - /* - * Attempt to find the inode. - */ - retry: - result = nfsd_iget(sb, ino, generation); - err = PTR_ERR(result); - if (IS_ERR(result)) - goto err_out; - err = -ESTALE; - if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) - return result; - - /* result is now an anonymous dentry, which may be adequate as it stands, or else - * will get spliced into the dcache tree */ - - if (!S_ISDIR(result->d_inode->i_mode) && ! needpath) { - nfsdstats.fh_anon++; - return result; - } - - /* It's a directory, or we are required to confirm the file's - * location in the tree. - */ - dprintk("nfs_fh: need to look harder for %d/%ld\n",sb->s_dev,ino); - down(&sb->s_nfsd_free_path_sem); - - /* claiming the semaphore might have allowed things to get fixed up */ - if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) { - up(&sb->s_nfsd_free_path_sem); - return result; - } - - - found = 0; - if (!S_ISDIR(result->d_inode->i_mode)) { - nfsdstats.fh_nocache_nondir++; - if (dirino == 0) - goto err_result; /* don't know how to find parent */ - else { - /* need to iget dirino and make sure this inode is in that directory */ - dentry = nfsd_iget(sb, dirino, 0); - err = PTR_ERR(dentry); - if (IS_ERR(dentry)) - goto err_result; - err = -ESTALE; - if (!dentry->d_inode - || !S_ISDIR(dentry->d_inode->i_mode)) { - goto err_dentry; - } - if ((!dentry->d_flags & DCACHE_NFSD_DISCONNECTED)) - found = 1; - tmp = splice(result, dentry); - err = PTR_ERR(tmp); - if (IS_ERR(tmp)) - goto err_dentry; - if (tmp != result) { - /* it is safe to just use tmp instead, but we must discard result first */ - d_drop(result); - dput(result); - result = tmp; - /* If !found, then this is really wierd, but it shouldn't hurt */ - } - } - } else { - nfsdstats.fh_nocache_dir++; - dentry = dget(result); - } - - while(!found) { - /* LOOP INVARIANT */ - /* haven't found a place in the tree yet, but we do have a free path - * from dentry down to result, and dentry is a directory. - * Have a hold on dentry and result */ - struct dentry *pdentry; - struct inode *parent; - - pdentry = nfsd_findparent(dentry); - err = PTR_ERR(pdentry); - if (IS_ERR(pdentry)) - goto err_dentry; - parent = pdentry->d_inode; - err = -EACCES; - if (!parent) { - dput(pdentry); - goto err_dentry; - } - - if (!(dentry->d_flags & DCACHE_NFSD_DISCONNECTED)) - found = 1; - - tmp = splice(dentry, pdentry); - if (tmp != dentry) { - /* Something wrong. We need to drop thw whole dentry->result path - * whatever it was - */ - struct dentry *d; - for (d=result ; d ; d=(d->d_parent == d)?NULL:d->d_parent) - d_drop(d); - } - if (IS_ERR(tmp)) { - err = PTR_ERR(tmp); - dput(pdentry); - goto err_dentry; - } - if (tmp != dentry) { - /* we lost a race, try again - */ - dput(tmp); - dput(dentry); - dput(result); /* this will discard the whole free path, so we can up the semaphore */ - up(&sb->s_nfsd_free_path_sem); - goto retry; - } - dput(dentry); - dentry = pdentry; - } - dput(dentry); - up(&sb->s_nfsd_free_path_sem); - return result; - -err_dentry: - dput(dentry); -err_result: - dput(result); - up(&sb->s_nfsd_free_path_sem); -err_out: - if (err == -ESTALE) - nfsdstats.fh_stale++; - return ERR_PTR(err); -} - /* * Perform sanity checks on the dentry in a client's file handle. * @@ -490,6 +44,8 @@ struct knfsd_fh *fh = &fhp->fh_handle; struct svc_export *exp; struct dentry *dentry; + struct fhandle vfs_fh; + struct super_block *sb; struct inode *inode; u32 error = 0; @@ -567,7 +123,11 @@ error = nfserr_badhandle; #endif + sb = exp->ex_dentry->d_inode->i_sb; if (fh->fh_version == 1) { + vfs_fh.id = datap[0]; + vfs_fh.version = datap[1]; + /* if fileid_type != 0, and super_operations provide fh_to_dentry lookup, * then should use that */ switch (fh->fh_fileid_type) { @@ -576,26 +136,32 @@ break; case 1: if ((data_left-=2)<0) goto out; - dentry = find_fh_dentry(exp->ex_dentry->d_inode->i_sb, - datap[0], datap[1], - 0, - !(exp->ex_flags & NFSEXP_NOSUBTREECHECK)); + if (sb->s_op->fhandle_to_dentry(sb, &vfs_fh, &dentry)) + goto out; break; case 2: if ((data_left-=3)<0) goto out; - dentry = find_fh_dentry(exp->ex_dentry->d_inode->i_sb, - datap[0], datap[1], - datap[2], - !(exp->ex_flags & NFSEXP_NOSUBTREECHECK)); + if (sb->s_op->fhandle_to_dentry(sb, &vfs_fh, &dentry)) + goto out; + /* + * We can add an additional check here to be + * sure that the parent did not changed. But + * it hardly can be worth the trouble. + */ break; default: goto out; } } else { - - dentry = find_fh_dentry(exp->ex_dentry->d_inode->i_sb, - fh->ofh_ino, fh->ofh_generation, - fh->ofh_dirino, - !(exp->ex_flags & NFSEXP_NOSUBTREECHECK)); + vfs_fh.id = fh->ofh_ino; + vfs_fh.version = fh->ofh_generation; + if (sb->s_op->fhandle_to_dentry(sb, &vfs_fh, &dentry)) + goto out; + + /* + * We can add an additional check here to be + * sure that the parent did not changed. But + * it hardly can be worth the trouble. + */ } if (IS_ERR(dentry)) { error = nfserrno(PTR_ERR(dentry)); @@ -645,39 +211,16 @@ * Security: Check that the export is valid for dentry */ error = 0; - - if (!(exp->ex_flags & NFSEXP_NOSUBTREECHECK)) { - if (exp->ex_dentry != dentry) { - struct dentry *tdentry = dentry; - - do { - tdentry = tdentry->d_parent; - if (exp->ex_dentry == tdentry) - break; - /* executable only by root and we can't be root */ - if (current->fsuid - && (exp->ex_flags & NFSEXP_ROOTSQUASH) - && !(tdentry->d_inode->i_uid - && (tdentry->d_inode->i_mode & S_IXUSR)) - && !(tdentry->d_inode->i_gid - && (tdentry->d_inode->i_mode & S_IXGRP)) - && !(tdentry->d_inode->i_mode & S_IXOTH) - ) { - error = nfserr_stale; - nfsdstats.fh_stale++; - dprintk("fh_verify: no root_squashed access.\n"); - } - } while ((tdentry != tdentry->d_parent)); - if (exp->ex_dentry != tdentry) { - error = nfserr_stale; - nfsdstats.fh_stale++; - printk("nfsd Security: %s/%s bad export.\n", - dentry->d_parent->d_name.name, - dentry->d_name.name); - goto out; - } - } - } + + /* + * Frankly, I see no point in all this security related business. Thus + * I've just thrown it away. But! In case I've missed some relevant + * parts of RFC[1094|1813] it is not very hard to bring it back. Well, + * as you can see all we have to do is to make an additional call to + * 'fhandle_to_dentry' asking it to resurrect parent directory of the + * particular file. And that's it. Than we'll get a complete branch + * that can be checked in any reasonable way. + */ /* Finally, check access permissions. */ if (!error) { @@ -704,17 +247,21 @@ __u32 **datapp, int maxsize) { __u32 *datap= *datapp; + struct fhandle vfs_fh; if (dentry == exp->ex_dentry) return 0; /* if super_operations provides dentry_to_fh lookup, should use that */ - *datap++ = ino_t_to_u32(dentry->d_inode->i_ino); - *datap++ = dentry->d_inode->i_generation; + dentry->d_sb->s_op->dentry_to_fhandle(dentry, &vfs_fh); + + *datap++ = ino_t_to_u32(vfs_fh.id); + *datap++ = ino_t_to_u32(vfs_fh.version); if (S_ISDIR(dentry->d_inode->i_mode) || (exp->ex_flags & NFSEXP_NOSUBTREECHECK)){ *datapp = datap; return 1; } - *datap++ = ino_t_to_u32(dentry->d_parent->d_inode->i_ino); + dentry->d_sb->s_op->dentry_to_fhandle(dentry->d_parent, &vfs_fh); + *datap++ = ino_t_to_u32(vfs_fh.id); *datapp = datap; return 2; } --- linux2/fs/ext2/super.c Thu Jun 15 18:19:49 2000 +++ linux/fs/ext2/super.c Thu Jun 15 18:35:09 2000 @@ -120,6 +120,9 @@ return; } +extern int ext2_dentry_to_fhandle(struct dentry *, struct fhandle *); +extern int ext2_fhandle_to_dentry(struct super_block *, struct fhandle *, struct dentry **); + static struct super_operations ext2_sops = { read_inode: ext2_read_inode, write_inode: ext2_write_inode, @@ -129,6 +132,8 @@ write_super: ext2_write_super, statfs: ext2_statfs, remount_fs: ext2_remount, + fhandle_to_dentry: ext2_fhandle_to_dentry, + dentry_to_fhandle: ext2_dentry_to_fhandle, }; /* --- linux2/fs/ext2/namei.c Thu Jun 15 18:19:49 2000 +++ linux/fs/ext2/namei.c Thu Jun 15 18:36:44 2000 @@ -21,8 +21,88 @@ #include #include #include +#include +#define NAME_ALLOC_LEN(len) ((len+16) & ~15) + +static inline +int still_partial_root(struct dentry *dentry) +{ + if ( dentry != dentry->d_parent ) { + up(&dentry->d_inode->i_zombie); + return 0; + } + return 1; +} + +static inline +struct dentry *try_d_alloc(struct inode *inode) +{ + struct dentry *dentry; + if (list_empty(&inode->i_dentry)) { + dentry = d_alloc(NULL, &(const struct qstr) {"", 0, 0}); + if (!dentry) + return dentry; + dentry->d_parent = dentry; + dentry->d_sb = inode->i_sb; + if (!list_empty(&inode->i_dentry)) { + d_delete(dentry); + dentry = dget(list_entry(inode->i_dentry.next, struct dentry, + d_alias)); + } else + d_add(dentry, inode); + } else + dentry = dget(list_entry(inode->i_dentry.next, struct dentry, d_alias)); + + return dentry; +} + + +static +int adopt_orphan(struct dentry *parent, struct dentry *son, const char *name, unsigned int namelen) +{ + char * str; + if (namelen > DNAME_INLINE_LEN-1) { + str = kmalloc(NAME_ALLOC_LEN(namelen), GFP_KERNEL); + if (!str) + return -1; + } else + str = son->d_iname; + + memcpy(str, name, namelen); + str[namelen] = 0; + + son->d_parent = dget(parent); + list_add(&son->d_child, &parent->d_subdirs); + son->d_name.name = str; + son->d_name.len = namelen; + son->d_name.hash = full_name_hash(name, namelen); + + list_del(&son->d_hash); + d_rehash(son); + + return 0; +} + +int ext2_dentry_to_fhandle(struct dentry *dentry, struct fhandle *fh) +{ + if (!dentry) { + printk("d2fh: ERROR: NULL dentry\n"); + fh->id = 0; + return -1; + } + if (!dentry->d_inode) { + printk("d2fh: ERROR: negative dentry\n"); + fh->id = 0; + return -1; + } + fh->id = dentry->d_inode->i_ino; + fh->version = dentry->d_inode->i_generation; + printk("d2fh: id %lu generation %lu\n", fh->id, fh->version); + return 0; +} + /* * define how far ahead to read directories while searching them. @@ -69,7 +149,7 @@ *res_dir = NULL; sb = dir->i_sb; - if (namelen > EXT2_NAME_LEN) + if (namelen > EXT2_NAME_LEN && name) return NULL; memset (bh_use, 0, sizeof (bh_use)); @@ -119,8 +199,9 @@ /* do minimal checking `by hand' */ int de_len; - if ((char *) de + namelen <= dlimit && - ext2_match (namelen, name, de)) { + if ((!name && le32_to_cpu(de->inode) == namelen) || + (name && (char *) de + namelen <= dlimit && + ext2_match (namelen, name, de))) { /* found a match - just to be sure, do a full check */ if (!ext2_check_dir_entry("ext2_find_entry", @@ -159,11 +240,138 @@ return NULL; } +int ext2_fhandle_to_dentry(struct super_block *sb, struct fhandle *fh, struct dentry **result) +{ + struct inode *inode; + struct dentry *dentry; + struct inode *iparent; + struct dentry *dparent; + + struct dentry *cursor; + struct buffer_head * bh; + struct ext2_dir_entry_2 * de; + unsigned long ino; + + printk("fh2d_1: id %lu generation %lu with root %p\n", fh->id, fh->version, sb->s_root); + + /* This code must go away when we implement proper ext2_iget & co. */ + inode = iget(sb, fh->id); + + if (!inode) { + printk("fh2d: ERROR empty inode\n"); + return -1; + } + + if (!S_ISDIR(inode->i_mode)) { + /* for files it's simple -- just allocate anonymous dentry and we are done */ + dentry = d_alloc(NULL, &(const struct qstr) {"", 0, 0}); + if (!dentry) + return -1; + dentry->d_parent = dentry; + dentry->d_sb = sb; + d_add(dentry, inode); + *result = dentry; + printk("fh2d_2: file dentry found\n"); + return 0; + } + + /* For directories we ought to do some additional work trying to connect + * the resulting dentry to the global root. The movement is accomplished + * in six stages: + */ + +/* STEP 1: Trying to allocate dentry for the newly created inode */ + + dentry = try_d_alloc(inode); + if (!dentry) + return -1; + +/* STEP 2: Trying to run towards the global root */ + +get_up: + + for ( cursor = dentry; cursor != cursor->d_parent; cursor = cursor->d_parent ); + + if (cursor != sb->s_root) { + +/* STEP 3: Trying to get inode of the parent directory ".." */ + printk("fh2d: down i_zombie %p\n", cursor->d_inode); + down(&cursor->d_inode->i_zombie); + printk("fh2d: down i_zombie %p\n", cursor->d_inode); + if (!still_partial_root(cursor)) + goto get_up; + +step_up: + bh = ext2_find_entry(cursor->d_inode, "..", 2, &de); + if (bh) { + ino = le32_to_cpu(de->inode); + brelse (bh); + iparent = iget(sb, ino); + if (!iparent) + return -1; + printk("fh2d: parent found %lu\n", ino); + } else { + /* Hm? What does it mean -- no parent ? */ + printk("fh2d: ERROR -- no parent\n"); + return -1; + } + +/* STEP 4: Trying to set up dentry for parent directory. */ + if (!still_partial_root(cursor)) { + iput(iparent); /* will be cleaned with ext2_iget */ + goto get_up; + } + dparent = try_d_alloc(iparent); + if (!dparent) + return -1; + +/* STEP 5: Establish relationship between parent and child. */ + if (!still_partial_root(cursor)) { + dput(dparent); + goto get_up; + } + + printk("fh2d: down i_zombie %p\n", iparent); + down(&iparent->i_zombie); + printk("fh2d: down i_zombie %p\n", iparent); + ino = cursor->d_inode->i_ino; + bh = ext2_find_entry(cursor->d_inode, NULL, ino, &de); + if (!bh) { + /* Hm? What does it mean -- no parent ? */ + printk("fh2d: ERROR -- no parent -- 2\n"); + return -1; + } + + if (!still_partial_root(cursor)) { + up(&iparent->i_zombie); + dput(dparent); + goto get_up; + } + + adopt_orphan(dparent, cursor, de->name, de->name_len); + brelse (bh); + +/* STEP 6: Move current to parent and continue the whole proceses. */ + up(&cursor->d_inode->i_zombie); + cursor = dparent; + if (cursor != cursor->d_parent || cursor == sb->s_root) { + up(&cursor->d_inode->i_zombie); + goto get_up; + } else + goto step_up; + } + + *result = dentry; + return 0; +} + + static struct dentry *ext2_lookup(struct inode * dir, struct dentry *dentry) { struct inode * inode; struct ext2_dir_entry_2 * de; struct buffer_head * bh; + struct dentry *proot; if (dentry->d_name.len > EXT2_NAME_LEN) return ERR_PTR(-ENAMETOOLONG); @@ -178,7 +386,26 @@ if (!inode) return ERR_PTR(-EACCES); } - d_add(dentry, inode); + + if ( inode ) { + printk("lookup: down %p %p\n", inode, dir); + down(&inode->i_zombie); + down(&dir->i_zombie); + printk("lookup: down %p %p\n", inode, dir); + if ( S_ISDIR(inode->i_mode) && !list_empty(&inode->i_dentry) ) { + proot = list_entry(inode->i_dentry.next, struct dentry, d_alias); + if ( proot == proot->d_parent ) + adopt_orphan(dentry->d_parent, proot, dentry->d_name.name, + dentry->d_name.len); + + d_delete(dentry); + } else + d_add(dentry, inode); + up(&dir->i_zombie); + up(&inode->i_zombie); + } else + d_add(dentry, inode); + return NULL; }