aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs
diff options
context:
space:
mode:
authorSteve French <sfrench@us.ibm.com>2008-09-23 14:23:33 -0400
committerSteve French <sfrench@us.ibm.com>2008-09-23 14:23:33 -0400
commitee2fd967fb23c5eecabc8a660ec66fcd79acbd47 (patch)
tree8a1922499e0775d4849d6ed8d4e3027800d7242d /fs/cifs
parent6d22f09896c0d62c003ffa25fff25323e3ed608b (diff)
[CIFS] fix busy-file renames and refactor cifs_rename logic
Break out the code that does the actual renaming into a separate function and have cifs_rename call that. That function will attempt a path based rename first and then do a filehandle based one if it looks like the source is busy. The existing logic tried a path based rename first, but if we needed to remove the destination then it only attempted a filehandle based rename afterward. Not all servers support renaming by filehandle, so we need to always attempt path rename first and fall back to filehandle rename if it doesn't work. This also fixes renames of open files on windows servers (at least when the source and destination directories are the same). CC: Jeff Layton <jlayton@redhat.com> Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs/cifs')
-rw-r--r--fs/cifs/inode.c186
1 files changed, 104 insertions, 82 deletions
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index 954b670f1687..82612be9477b 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -806,8 +806,6 @@ cifs_rename_pending_delete(char *full_path, struct inode *inode, int xid)
806 rc = CIFSSMBRenameOpenFile(xid, tcon, netfid, NULL, cifs_sb->local_nls, 806 rc = CIFSSMBRenameOpenFile(xid, tcon, netfid, NULL, cifs_sb->local_nls,
807 cifs_sb->mnt_cifs_flags & 807 cifs_sb->mnt_cifs_flags &
808 CIFS_MOUNT_MAP_SPECIAL_CHR); 808 CIFS_MOUNT_MAP_SPECIAL_CHR);
809 if (rc != 0)
810 goto out_close;
811 809
812 /* set DELETE_ON_CLOSE */ 810 /* set DELETE_ON_CLOSE */
813 rc = CIFSSMBSetFileDisposition(xid, tcon, true, netfid, current->tgid); 811 rc = CIFSSMBSetFileDisposition(xid, tcon, true, netfid, current->tgid);
@@ -1180,117 +1178,141 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
1180 return rc; 1178 return rc;
1181} 1179}
1182 1180
1181static int
1182cifs_do_rename(int xid, struct dentry *from_dentry, const char *fromPath,
1183 struct dentry *to_dentry, const char *toPath)
1184{
1185 struct cifs_sb_info *cifs_sb = CIFS_SB(from_dentry->d_sb);
1186 struct cifsTconInfo *pTcon = cifs_sb->tcon;
1187 __u16 srcfid;
1188 int oplock, rc;
1189
1190 /* try path-based rename first */
1191 rc = CIFSSMBRename(xid, pTcon, fromPath, toPath, cifs_sb->local_nls,
1192 cifs_sb->mnt_cifs_flags &
1193 CIFS_MOUNT_MAP_SPECIAL_CHR);
1194
1195 /*
1196 * don't bother with rename by filehandle unless file is busy and
1197 * source Note that cross directory moves do not work with
1198 * rename by filehandle to various Windows servers.
1199 */
1200 if (rc == 0 || rc != -ETXTBSY)
1201 return rc;
1202
1203 /* open the file to be renamed -- we need DELETE perms */
1204 rc = CIFSSMBOpen(xid, pTcon, fromPath, FILE_OPEN, DELETE,
1205 CREATE_NOT_DIR, &srcfid, &oplock, NULL,
1206 cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
1207 CIFS_MOUNT_MAP_SPECIAL_CHR);
1208
1209 if (rc == 0) {
1210 rc = CIFSSMBRenameOpenFile(xid, pTcon, srcfid,
1211 (const char *) to_dentry->d_name.name,
1212 cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
1213 CIFS_MOUNT_MAP_SPECIAL_CHR);
1214
1215 CIFSSMBClose(xid, pTcon, srcfid);
1216 }
1217
1218 return rc;
1219}
1220
1183int cifs_rename(struct inode *source_inode, struct dentry *source_direntry, 1221int cifs_rename(struct inode *source_inode, struct dentry *source_direntry,
1184 struct inode *target_inode, struct dentry *target_direntry) 1222 struct inode *target_inode, struct dentry *target_direntry)
1185{ 1223{
1186 char *fromName; 1224 char *fromName = NULL;
1187 char *toName; 1225 char *toName = NULL;
1188 struct cifs_sb_info *cifs_sb_source; 1226 struct cifs_sb_info *cifs_sb_source;
1189 struct cifs_sb_info *cifs_sb_target; 1227 struct cifs_sb_info *cifs_sb_target;
1190 struct cifsTconInfo *pTcon; 1228 struct cifsTconInfo *pTcon;
1229 FILE_UNIX_BASIC_INFO *info_buf_source = NULL;
1230 FILE_UNIX_BASIC_INFO *info_buf_target;
1191 int xid; 1231 int xid;
1192 int rc = 0; 1232 int rc;
1193
1194 xid = GetXid();
1195 1233
1196 cifs_sb_target = CIFS_SB(target_inode->i_sb); 1234 cifs_sb_target = CIFS_SB(target_inode->i_sb);
1197 cifs_sb_source = CIFS_SB(source_inode->i_sb); 1235 cifs_sb_source = CIFS_SB(source_inode->i_sb);
1198 pTcon = cifs_sb_source->tcon; 1236 pTcon = cifs_sb_source->tcon;
1199 1237
1238 xid = GetXid();
1239
1240 /*
1241 * BB: this might be allowed if same server, but different share.
1242 * Consider adding support for this
1243 */
1200 if (pTcon != cifs_sb_target->tcon) { 1244 if (pTcon != cifs_sb_target->tcon) {
1201 FreeXid(xid); 1245 rc = -EXDEV;
1202 return -EXDEV; /* BB actually could be allowed if same server, 1246 goto cifs_rename_exit;
1203 but different share.
1204 Might eventually add support for this */
1205 } 1247 }
1206 1248
1207 /* we already have the rename sem so we do not need to grab it again 1249 /*
1208 here to protect the path integrity */ 1250 * we already have the rename sem so we do not need to
1251 * grab it again here to protect the path integrity
1252 */
1209 fromName = build_path_from_dentry(source_direntry); 1253 fromName = build_path_from_dentry(source_direntry);
1254 if (fromName == NULL) {
1255 rc = -ENOMEM;
1256 goto cifs_rename_exit;
1257 }
1258
1210 toName = build_path_from_dentry(target_direntry); 1259 toName = build_path_from_dentry(target_direntry);
1211 if ((fromName == NULL) || (toName == NULL)) { 1260 if (toName == NULL) {
1212 rc = -ENOMEM; 1261 rc = -ENOMEM;
1213 goto cifs_rename_exit; 1262 goto cifs_rename_exit;
1214 } 1263 }
1215 1264
1216 rc = CIFSSMBRename(xid, pTcon, fromName, toName, 1265 rc = cifs_do_rename(xid, source_direntry, fromName,
1217 cifs_sb_source->local_nls, 1266 target_direntry, toName);
1218 cifs_sb_source->mnt_cifs_flags & 1267
1219 CIFS_MOUNT_MAP_SPECIAL_CHR);
1220 if (rc == -EEXIST) { 1268 if (rc == -EEXIST) {
1221 /* check if they are the same file because rename of hardlinked 1269 if (pTcon->unix_ext) {
1222 files is a noop */ 1270 /*
1223 FILE_UNIX_BASIC_INFO *info_buf_source; 1271 * Are src and dst hardlinks of same inode? We can
1224 FILE_UNIX_BASIC_INFO *info_buf_target; 1272 * only tell with unix extensions enabled
1225 1273 */
1226 info_buf_source = 1274 info_buf_source =
1227 kmalloc(2 * sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL); 1275 kmalloc(2 * sizeof(FILE_UNIX_BASIC_INFO),
1228 if (info_buf_source != NULL) { 1276 GFP_KERNEL);
1277 if (info_buf_source != NULL)
1278 goto unlink_target;
1279
1229 info_buf_target = info_buf_source + 1; 1280 info_buf_target = info_buf_source + 1;
1230 if (pTcon->unix_ext) 1281 rc = CIFSSMBUnixQPathInfo(xid, pTcon, fromName,
1231 rc = CIFSSMBUnixQPathInfo(xid, pTcon, fromName, 1282 info_buf_source,
1232 info_buf_source, 1283 cifs_sb_source->local_nls,
1233 cifs_sb_source->local_nls, 1284 cifs_sb_source->mnt_cifs_flags &
1234 cifs_sb_source->mnt_cifs_flags &
1235 CIFS_MOUNT_MAP_SPECIAL_CHR); 1285 CIFS_MOUNT_MAP_SPECIAL_CHR);
1236 /* else rc is still EEXIST so will fall through to 1286 if (rc != 0)
1237 unlink the target and retry rename */ 1287 goto unlink_target;
1238 if (rc == 0) { 1288
1239 rc = CIFSSMBUnixQPathInfo(xid, pTcon, toName, 1289 rc = CIFSSMBUnixQPathInfo(xid, pTcon,
1240 info_buf_target, 1290 toName, info_buf_target,
1241 cifs_sb_target->local_nls, 1291 cifs_sb_target->local_nls,
1242 /* remap based on source sb */ 1292 /* remap based on source sb */
1243 cifs_sb_source->mnt_cifs_flags & 1293 cifs_sb_source->mnt_cifs_flags &
1244 CIFS_MOUNT_MAP_SPECIAL_CHR);
1245 }
1246 if ((rc == 0) &&
1247 (info_buf_source->UniqueId ==
1248 info_buf_target->UniqueId)) {
1249 /* do not rename since the files are hardlinked which
1250 is a noop */
1251 } else {
1252 /* we either can not tell the files are hardlinked
1253 (as with Windows servers) or files are not
1254 hardlinked so delete the target manually before
1255 renaming to follow POSIX rather than Windows
1256 semantics */
1257 cifs_unlink(target_inode, target_direntry);
1258 rc = CIFSSMBRename(xid, pTcon, fromName,
1259 toName,
1260 cifs_sb_source->local_nls,
1261 cifs_sb_source->mnt_cifs_flags
1262 & CIFS_MOUNT_MAP_SPECIAL_CHR);
1263 }
1264 kfree(info_buf_source);
1265 } /* if we can not get memory just leave rc as EEXIST */
1266 }
1267
1268 if (rc)
1269 cFYI(1, ("rename rc %d", rc));
1270
1271 if ((rc == -EIO) || (rc == -EEXIST)) {
1272 int oplock = 0;
1273 __u16 netfid;
1274
1275 /* BB FIXME Is Generic Read correct for rename? */
1276 /* if renaming directory - we should not say CREATE_NOT_DIR,
1277 need to test renaming open directory, also GENERIC_READ
1278 might not right be right access to request */
1279 rc = CIFSSMBOpen(xid, pTcon, fromName, FILE_OPEN, GENERIC_READ,
1280 CREATE_NOT_DIR, &netfid, &oplock, NULL,
1281 cifs_sb_source->local_nls,
1282 cifs_sb_source->mnt_cifs_flags &
1283 CIFS_MOUNT_MAP_SPECIAL_CHR);
1284 if (rc == 0) {
1285 rc = CIFSSMBRenameOpenFile(xid, pTcon, netfid, toName,
1286 cifs_sb_source->local_nls,
1287 cifs_sb_source->mnt_cifs_flags &
1288 CIFS_MOUNT_MAP_SPECIAL_CHR); 1294 CIFS_MOUNT_MAP_SPECIAL_CHR);
1289 CIFSSMBClose(xid, pTcon, netfid); 1295
1290 } 1296 if (rc == 0 && (info_buf_source->UniqueId ==
1297 info_buf_target->UniqueId))
1298 /* same file, POSIX says that this is a noop */
1299 goto cifs_rename_exit;
1300 } /* else ... BB we could add the same check for Windows by
1301 checking the UniqueId via FILE_INTERNAL_INFO */
1302unlink_target:
1303 /*
1304 * we either can not tell the files are hardlinked (as with
1305 * Windows servers) or files are not hardlinked. Delete the
1306 * target manually before renaming to follow POSIX rather than
1307 * Windows semantics
1308 */
1309 cifs_unlink(target_inode, target_direntry);
1310 rc = cifs_do_rename(xid, source_direntry, fromName,
1311 target_direntry, toName);
1291 } 1312 }
1292 1313
1293cifs_rename_exit: 1314cifs_rename_exit:
1315 kfree(info_buf_source);
1294 kfree(fromName); 1316 kfree(fromName);
1295 kfree(toName); 1317 kfree(toName);
1296 FreeXid(xid); 1318 FreeXid(xid);