* Set dentry and possibly attribute timeouts from the lookup/mk*
  * replies
  */
-static void fuse_change_timeout(struct dentry *entry, struct fuse_entry_out *o)
+static void fuse_change_entry_timeout(struct dentry *entry,
+                                     struct fuse_entry_out *o)
 {
        fuse_dentry_settime(entry,
                time_to_jiffies(o->entry_valid, o->entry_valid_nsec));
-       if (entry->d_inode)
-               get_fuse_inode(entry->d_inode)->i_time =
-                       time_to_jiffies(o->attr_valid, o->attr_valid_nsec);
+}
+
+static u64 attr_timeout(struct fuse_attr_out *o)
+{
+       return time_to_jiffies(o->attr_valid, o->attr_valid_nsec);
+}
+
+static u64 entry_attr_timeout(struct fuse_entry_out *o)
+{
+       return time_to_jiffies(o->attr_valid, o->attr_valid_nsec);
 }
 
 /*
                struct fuse_req *req;
                struct fuse_req *forget_req;
                struct dentry *parent;
+               u64 attr_version;
 
                /* For negative dentries, always do a fresh lookup */
                if (!inode)
                        return 0;
                }
 
+               spin_lock(&fc->lock);
+               attr_version = fc->attr_version;
+               spin_unlock(&fc->lock);
+
                parent = dget_parent(entry);
                fuse_lookup_init(req, parent->d_inode, entry, &outarg);
                request_send(fc, req);
                if (err || (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
                        return 0;
 
-               fuse_change_attributes(inode, &outarg.attr);
-               fuse_change_timeout(entry, &outarg);
+               fuse_change_attributes(inode, &outarg.attr,
+                                      entry_attr_timeout(&outarg),
+                                      attr_version);
+               fuse_change_entry_timeout(entry, &outarg);
        }
        return 1;
 }
        struct fuse_conn *fc = get_fuse_conn(dir);
        struct fuse_req *req;
        struct fuse_req *forget_req;
+       u64 attr_version;
 
        if (entry->d_name.len > FUSE_NAME_MAX)
                return ERR_PTR(-ENAMETOOLONG);
                return ERR_PTR(PTR_ERR(forget_req));
        }
 
+       spin_lock(&fc->lock);
+       attr_version = fc->attr_version;
+       spin_unlock(&fc->lock);
+
        fuse_lookup_init(req, dir, entry, &outarg);
        request_send(fc, req);
        err = req->out.h.error;
                err = -EIO;
        if (!err && outarg.nodeid) {
                inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation,
-                                 &outarg.attr);
+                                 &outarg.attr, entry_attr_timeout(&outarg),
+                                 attr_version);
                if (!inode) {
                        fuse_send_forget(fc, forget_req, outarg.nodeid, 1);
                        return ERR_PTR(-ENOMEM);
 
        entry->d_op = &fuse_dentry_operations;
        if (!err)
-               fuse_change_timeout(entry, &outarg);
+               fuse_change_entry_timeout(entry, &outarg);
        else
                fuse_invalidate_entry_cache(entry);
        return NULL;
 
        fuse_put_request(fc, req);
        inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation,
-                         &outentry.attr);
+                         &outentry.attr, entry_attr_timeout(&outentry), 0);
        if (!inode) {
                flags &= ~(O_CREAT | O_EXCL | O_TRUNC);
                ff->fh = outopen.fh;
        }
        fuse_put_request(fc, forget_req);
        d_instantiate(entry, inode);
-       fuse_change_timeout(entry, &outentry);
+       fuse_change_entry_timeout(entry, &outentry);
        file = lookup_instantiate_filp(nd, entry, generic_file_open);
        if (IS_ERR(file)) {
                ff->fh = outopen.fh;
                goto out_put_forget_req;
 
        inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation,
-                         &outarg.attr);
+                         &outarg.attr, entry_attr_timeout(&outarg), 0);
        if (!inode) {
                fuse_send_forget(fc, forget_req, outarg.nodeid, 1);
                return -ENOMEM;
        } else
                d_instantiate(entry, inode);
 
-       fuse_change_timeout(entry, &outarg);
+       fuse_change_entry_timeout(entry, &outarg);
        fuse_invalidate_attr(dir);
        return 0;
 
        return err;
 }
 
-static int fuse_do_getattr(struct inode *inode)
+static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr,
+                         struct kstat *stat)
+{
+       stat->dev = inode->i_sb->s_dev;
+       stat->ino = attr->ino;
+       stat->mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777);
+       stat->nlink = attr->nlink;
+       stat->uid = attr->uid;
+       stat->gid = attr->gid;
+       stat->rdev = inode->i_rdev;
+       stat->atime.tv_sec = attr->atime;
+       stat->atime.tv_nsec = attr->atimensec;
+       stat->mtime.tv_sec = attr->mtime;
+       stat->mtime.tv_nsec = attr->mtimensec;
+       stat->ctime.tv_sec = attr->ctime;
+       stat->ctime.tv_nsec = attr->ctimensec;
+       stat->size = attr->size;
+       stat->blocks = attr->blocks;
+       stat->blksize = (1 << inode->i_blkbits);
+}
+
+static int fuse_do_getattr(struct inode *inode, struct kstat *stat)
 {
        int err;
        struct fuse_attr_out arg;
        struct fuse_conn *fc = get_fuse_conn(inode);
-       struct fuse_req *req = fuse_get_req(fc);
+       struct fuse_req *req;
+       u64 attr_version;
+
+       req = fuse_get_req(fc);
        if (IS_ERR(req))
                return PTR_ERR(req);
 
+       spin_lock(&fc->lock);
+       attr_version = fc->attr_version;
+       spin_unlock(&fc->lock);
+
        req->in.h.opcode = FUSE_GETATTR;
        req->in.h.nodeid = get_node_id(inode);
        req->out.numargs = 1;
                        make_bad_inode(inode);
                        err = -EIO;
                } else {
-                       struct fuse_inode *fi = get_fuse_inode(inode);
-                       fuse_change_attributes(inode, &arg.attr);
-                       fi->i_time = time_to_jiffies(arg.attr_valid,
-                                                    arg.attr_valid_nsec);
+                       fuse_change_attributes(inode, &arg.attr,
+                                              attr_timeout(&arg),
+                                              attr_version);
+                       if (stat)
+                               fuse_fillattr(inode, &arg.attr, stat);
                }
        }
        return err;
 }
 
-/*
- * Check if attributes are still valid, and if not send a GETATTR
- * request to refresh them.
- */
-static int fuse_refresh_attributes(struct inode *inode)
-{
-       struct fuse_inode *fi = get_fuse_inode(inode);
-
-       if (fi->i_time < get_jiffies_64())
-               return fuse_do_getattr(inode);
-       else
-               return 0;
-}
-
 /*
  * Calling into a user-controlled filesystem gives the filesystem
  * daemon ptrace-like capabilities over the requester process.  This
         */
        if ((fc->flags & FUSE_DEFAULT_PERMISSIONS) ||
            ((mask & MAY_EXEC) && S_ISREG(inode->i_mode))) {
-               err = fuse_refresh_attributes(inode);
-               if (err)
-                       return err;
+               struct fuse_inode *fi = get_fuse_inode(inode);
+               if (fi->i_time < get_jiffies_64()) {
+                       err = fuse_do_getattr(inode, NULL);
+                       if (err)
+                               return err;
 
-               refreshed = true;
+                       refreshed = true;
+               }
        }
 
        if (fc->flags & FUSE_DEFAULT_PERMISSIONS) {
                   attributes.  This is also needed, because the root
                   node will at first have no permissions */
                if (err == -EACCES && !refreshed) {
-                       err = fuse_do_getattr(inode);
+                       err = fuse_do_getattr(inode, NULL);
                        if (!err)
                                err = generic_permission(inode, mask, NULL);
                }
                        if (refreshed)
                                return -EACCES;
 
-                       err = fuse_do_getattr(inode);
+                       err = fuse_do_getattr(inode, NULL);
                        if (!err && !(inode->i_mode & S_IXUGO))
                                return -EACCES;
                }
 {
        struct inode *inode = entry->d_inode;
        struct fuse_conn *fc = get_fuse_conn(inode);
-       struct fuse_inode *fi = get_fuse_inode(inode);
        struct fuse_req *req;
        struct fuse_setattr_in inarg;
        struct fuse_attr_out outarg;
                return -EIO;
        }
 
-       fuse_change_attributes(inode, &outarg.attr);
-       fi->i_time = time_to_jiffies(outarg.attr_valid, outarg.attr_valid_nsec);
+       fuse_change_attributes(inode, &outarg.attr, attr_timeout(&outarg), 0);
        return 0;
 }
 
        if (!fuse_allow_task(fc, current))
                return -EACCES;
 
-       err = fuse_refresh_attributes(inode);
-       if (!err) {
+       if (fi->i_time < get_jiffies_64())
+               err = fuse_do_getattr(inode, stat);
+       else {
+               err = 0;
                generic_fillattr(inode, stat);
                stat->mode = fi->orig_i_mode;
        }
 
        unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1);
 }
 
-void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr)
+
+void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
+                           u64 attr_valid, u64 attr_version)
 {
        struct fuse_conn *fc = get_fuse_conn(inode);
        struct fuse_inode *fi = get_fuse_inode(inode);
        loff_t oldsize;
 
+       spin_lock(&fc->lock);
+       if (attr_version != 0 && fi->attr_version > attr_version) {
+               spin_unlock(&fc->lock);
+               return;
+       }
+       fi->attr_version = ++fc->attr_version;
+       fi->i_time = attr_valid;
+
        inode->i_ino     = attr->ino;
        inode->i_mode    = (inode->i_mode & S_IFMT) | (attr->mode & 07777);
        inode->i_nlink   = attr->nlink;
        if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS))
                inode->i_mode &= ~S_ISVTX;
 
-       spin_lock(&fc->lock);
        oldsize = inode->i_size;
        i_size_write(inode, attr->size);
        spin_unlock(&fc->lock);
 }
 
 struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
-                       int generation, struct fuse_attr *attr)
+                       int generation, struct fuse_attr *attr,
+                       u64 attr_valid, u64 attr_version)
 {
        struct inode *inode;
        struct fuse_inode *fi;
        spin_lock(&fc->lock);
        fi->nlookup ++;
        spin_unlock(&fc->lock);
-       fuse_change_attributes(inode, attr);
+       fuse_change_attributes(inode, attr, attr_valid, attr_version);
+
        return inode;
 }
 
                }
                fc->reqctr = 0;
                fc->blocked = 1;
+               fc->attr_version = 1;
                get_random_bytes(&fc->scramble_key, sizeof(fc->scramble_key));
        }
 out:
        attr.mode = mode;
        attr.ino = FUSE_ROOT_ID;
        attr.nlink = 1;
-       return fuse_iget(sb, 1, 0, &attr);
+       return fuse_iget(sb, 1, 0, &attr, 0, 0);
 }
 
 static const struct super_operations fuse_super_operations = {