diff options
Diffstat (limited to 'fs/fuse/dir.c')
-rw-r--r-- | fs/fuse/dir.c | 51 |
1 files changed, 36 insertions, 15 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 0eda52738ec4..72a5d5b04494 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c | |||
@@ -1223,30 +1223,46 @@ static int fuse_direntplus_link(struct file *file, | |||
1223 | if (name.name[1] == '.' && name.len == 2) | 1223 | if (name.name[1] == '.' && name.len == 2) |
1224 | return 0; | 1224 | return 0; |
1225 | } | 1225 | } |
1226 | |||
1227 | if (invalid_nodeid(o->nodeid)) | ||
1228 | return -EIO; | ||
1229 | if (!fuse_valid_type(o->attr.mode)) | ||
1230 | return -EIO; | ||
1231 | |||
1226 | fc = get_fuse_conn(dir); | 1232 | fc = get_fuse_conn(dir); |
1227 | 1233 | ||
1228 | name.hash = full_name_hash(name.name, name.len); | 1234 | name.hash = full_name_hash(name.name, name.len); |
1229 | dentry = d_lookup(parent, &name); | 1235 | dentry = d_lookup(parent, &name); |
1230 | if (dentry && dentry->d_inode) { | 1236 | if (dentry) { |
1231 | inode = dentry->d_inode; | 1237 | inode = dentry->d_inode; |
1232 | if (get_node_id(inode) == o->nodeid) { | 1238 | if (!inode) { |
1239 | d_drop(dentry); | ||
1240 | } else if (get_node_id(inode) != o->nodeid || | ||
1241 | ((o->attr.mode ^ inode->i_mode) & S_IFMT)) { | ||
1242 | err = d_invalidate(dentry); | ||
1243 | if (err) | ||
1244 | goto out; | ||
1245 | } else if (is_bad_inode(inode)) { | ||
1246 | err = -EIO; | ||
1247 | goto out; | ||
1248 | } else { | ||
1233 | struct fuse_inode *fi; | 1249 | struct fuse_inode *fi; |
1234 | fi = get_fuse_inode(inode); | 1250 | fi = get_fuse_inode(inode); |
1235 | spin_lock(&fc->lock); | 1251 | spin_lock(&fc->lock); |
1236 | fi->nlookup++; | 1252 | fi->nlookup++; |
1237 | spin_unlock(&fc->lock); | 1253 | spin_unlock(&fc->lock); |
1238 | 1254 | ||
1255 | fuse_change_attributes(inode, &o->attr, | ||
1256 | entry_attr_timeout(o), | ||
1257 | attr_version); | ||
1258 | |||
1239 | /* | 1259 | /* |
1240 | * The other branch to 'found' comes via fuse_iget() | 1260 | * The other branch to 'found' comes via fuse_iget() |
1241 | * which bumps nlookup inside | 1261 | * which bumps nlookup inside |
1242 | */ | 1262 | */ |
1243 | goto found; | 1263 | goto found; |
1244 | } | 1264 | } |
1245 | err = d_invalidate(dentry); | ||
1246 | if (err) | ||
1247 | goto out; | ||
1248 | dput(dentry); | 1265 | dput(dentry); |
1249 | dentry = NULL; | ||
1250 | } | 1266 | } |
1251 | 1267 | ||
1252 | dentry = d_alloc(parent, &name); | 1268 | dentry = d_alloc(parent, &name); |
@@ -1259,25 +1275,30 @@ static int fuse_direntplus_link(struct file *file, | |||
1259 | if (!inode) | 1275 | if (!inode) |
1260 | goto out; | 1276 | goto out; |
1261 | 1277 | ||
1262 | alias = d_materialise_unique(dentry, inode); | 1278 | if (S_ISDIR(inode->i_mode)) { |
1263 | err = PTR_ERR(alias); | 1279 | mutex_lock(&fc->inst_mutex); |
1264 | if (IS_ERR(alias)) | 1280 | alias = fuse_d_add_directory(dentry, inode); |
1265 | goto out; | 1281 | mutex_unlock(&fc->inst_mutex); |
1282 | err = PTR_ERR(alias); | ||
1283 | if (IS_ERR(alias)) { | ||
1284 | iput(inode); | ||
1285 | goto out; | ||
1286 | } | ||
1287 | } else { | ||
1288 | alias = d_splice_alias(inode, dentry); | ||
1289 | } | ||
1290 | |||
1266 | if (alias) { | 1291 | if (alias) { |
1267 | dput(dentry); | 1292 | dput(dentry); |
1268 | dentry = alias; | 1293 | dentry = alias; |
1269 | } | 1294 | } |
1270 | 1295 | ||
1271 | found: | 1296 | found: |
1272 | fuse_change_attributes(inode, &o->attr, entry_attr_timeout(o), | ||
1273 | attr_version); | ||
1274 | |||
1275 | fuse_change_entry_timeout(dentry, o); | 1297 | fuse_change_entry_timeout(dentry, o); |
1276 | 1298 | ||
1277 | err = 0; | 1299 | err = 0; |
1278 | out: | 1300 | out: |
1279 | if (dentry) | 1301 | dput(dentry); |
1280 | dput(dentry); | ||
1281 | return err; | 1302 | return err; |
1282 | } | 1303 | } |
1283 | 1304 | ||