diff options
Diffstat (limited to 'fs/fuse/dir.c')
-rw-r--r-- | fs/fuse/dir.c | 84 |
1 files changed, 81 insertions, 3 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index c4807b3fc8a3..48b9971ecd97 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c | |||
@@ -1107,6 +1107,50 @@ static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg) | |||
1107 | } | 1107 | } |
1108 | 1108 | ||
1109 | /* | 1109 | /* |
1110 | * Prevent concurrent writepages on inode | ||
1111 | * | ||
1112 | * This is done by adding a negative bias to the inode write counter | ||
1113 | * and waiting for all pending writes to finish. | ||
1114 | */ | ||
1115 | void fuse_set_nowrite(struct inode *inode) | ||
1116 | { | ||
1117 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
1118 | struct fuse_inode *fi = get_fuse_inode(inode); | ||
1119 | |||
1120 | BUG_ON(!mutex_is_locked(&inode->i_mutex)); | ||
1121 | |||
1122 | spin_lock(&fc->lock); | ||
1123 | BUG_ON(fi->writectr < 0); | ||
1124 | fi->writectr += FUSE_NOWRITE; | ||
1125 | spin_unlock(&fc->lock); | ||
1126 | wait_event(fi->page_waitq, fi->writectr == FUSE_NOWRITE); | ||
1127 | } | ||
1128 | |||
1129 | /* | ||
1130 | * Allow writepages on inode | ||
1131 | * | ||
1132 | * Remove the bias from the writecounter and send any queued | ||
1133 | * writepages. | ||
1134 | */ | ||
1135 | static void __fuse_release_nowrite(struct inode *inode) | ||
1136 | { | ||
1137 | struct fuse_inode *fi = get_fuse_inode(inode); | ||
1138 | |||
1139 | BUG_ON(fi->writectr != FUSE_NOWRITE); | ||
1140 | fi->writectr = 0; | ||
1141 | fuse_flush_writepages(inode); | ||
1142 | } | ||
1143 | |||
1144 | void fuse_release_nowrite(struct inode *inode) | ||
1145 | { | ||
1146 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
1147 | |||
1148 | spin_lock(&fc->lock); | ||
1149 | __fuse_release_nowrite(inode); | ||
1150 | spin_unlock(&fc->lock); | ||
1151 | } | ||
1152 | |||
1153 | /* | ||
1110 | * Set attributes, and at the same time refresh them. | 1154 | * Set attributes, and at the same time refresh them. |
1111 | * | 1155 | * |
1112 | * Truncation is slightly complicated, because the 'truncate' request | 1156 | * Truncation is slightly complicated, because the 'truncate' request |
@@ -1122,6 +1166,8 @@ static int fuse_do_setattr(struct dentry *entry, struct iattr *attr, | |||
1122 | struct fuse_req *req; | 1166 | struct fuse_req *req; |
1123 | struct fuse_setattr_in inarg; | 1167 | struct fuse_setattr_in inarg; |
1124 | struct fuse_attr_out outarg; | 1168 | struct fuse_attr_out outarg; |
1169 | bool is_truncate = false; | ||
1170 | loff_t oldsize; | ||
1125 | int err; | 1171 | int err; |
1126 | 1172 | ||
1127 | if (!fuse_allow_task(fc, current)) | 1173 | if (!fuse_allow_task(fc, current)) |
@@ -1145,12 +1191,16 @@ static int fuse_do_setattr(struct dentry *entry, struct iattr *attr, | |||
1145 | send_sig(SIGXFSZ, current, 0); | 1191 | send_sig(SIGXFSZ, current, 0); |
1146 | return -EFBIG; | 1192 | return -EFBIG; |
1147 | } | 1193 | } |
1194 | is_truncate = true; | ||
1148 | } | 1195 | } |
1149 | 1196 | ||
1150 | req = fuse_get_req(fc); | 1197 | req = fuse_get_req(fc); |
1151 | if (IS_ERR(req)) | 1198 | if (IS_ERR(req)) |
1152 | return PTR_ERR(req); | 1199 | return PTR_ERR(req); |
1153 | 1200 | ||
1201 | if (is_truncate) | ||
1202 | fuse_set_nowrite(inode); | ||
1203 | |||
1154 | memset(&inarg, 0, sizeof(inarg)); | 1204 | memset(&inarg, 0, sizeof(inarg)); |
1155 | memset(&outarg, 0, sizeof(outarg)); | 1205 | memset(&outarg, 0, sizeof(outarg)); |
1156 | iattr_to_fattr(attr, &inarg); | 1206 | iattr_to_fattr(attr, &inarg); |
@@ -1181,16 +1231,44 @@ static int fuse_do_setattr(struct dentry *entry, struct iattr *attr, | |||
1181 | if (err) { | 1231 | if (err) { |
1182 | if (err == -EINTR) | 1232 | if (err == -EINTR) |
1183 | fuse_invalidate_attr(inode); | 1233 | fuse_invalidate_attr(inode); |
1184 | return err; | 1234 | goto error; |
1185 | } | 1235 | } |
1186 | 1236 | ||
1187 | if ((inode->i_mode ^ outarg.attr.mode) & S_IFMT) { | 1237 | if ((inode->i_mode ^ outarg.attr.mode) & S_IFMT) { |
1188 | make_bad_inode(inode); | 1238 | make_bad_inode(inode); |
1189 | return -EIO; | 1239 | err = -EIO; |
1240 | goto error; | ||
1241 | } | ||
1242 | |||
1243 | spin_lock(&fc->lock); | ||
1244 | fuse_change_attributes_common(inode, &outarg.attr, | ||
1245 | attr_timeout(&outarg)); | ||
1246 | oldsize = inode->i_size; | ||
1247 | i_size_write(inode, outarg.attr.size); | ||
1248 | |||
1249 | if (is_truncate) { | ||
1250 | /* NOTE: this may release/reacquire fc->lock */ | ||
1251 | __fuse_release_nowrite(inode); | ||
1252 | } | ||
1253 | spin_unlock(&fc->lock); | ||
1254 | |||
1255 | /* | ||
1256 | * Only call invalidate_inode_pages2() after removing | ||
1257 | * FUSE_NOWRITE, otherwise fuse_launder_page() would deadlock. | ||
1258 | */ | ||
1259 | if (S_ISREG(inode->i_mode) && oldsize != outarg.attr.size) { | ||
1260 | if (outarg.attr.size < oldsize) | ||
1261 | fuse_truncate(inode->i_mapping, outarg.attr.size); | ||
1262 | invalidate_inode_pages2(inode->i_mapping); | ||
1190 | } | 1263 | } |
1191 | 1264 | ||
1192 | fuse_change_attributes(inode, &outarg.attr, attr_timeout(&outarg), 0); | ||
1193 | return 0; | 1265 | return 0; |
1266 | |||
1267 | error: | ||
1268 | if (is_truncate) | ||
1269 | fuse_release_nowrite(inode); | ||
1270 | |||
1271 | return err; | ||
1194 | } | 1272 | } |
1195 | 1273 | ||
1196 | static int fuse_setattr(struct dentry *entry, struct iattr *attr) | 1274 | static int fuse_setattr(struct dentry *entry, struct iattr *attr) |