Skip to content

Commit b9857cd

Browse files
committed
Add validation to ensure we don't mv a file onto itself
Fixes aws#831
1 parent d421dc2 commit b9857cd

3 files changed

Lines changed: 84 additions & 0 deletions

File tree

awscli/customizations/s3/s3.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,23 @@ def add_paths(self, paths):
662662
self.parameters['dest'] = paths[1]
663663
elif len(paths) == 1:
664664
self.parameters['dest'] = paths[0]
665+
self._validate_path_args()
666+
667+
def _validate_path_args(self):
668+
# If we're using a mv command, you can't copy the object onto itself.
669+
params = self.parameters
670+
if self.cmd == 'mv' and self._same_path(params['src'], params['dest']):
671+
raise ValueError("Cannot mv a file onto itself: '%s' - '%s'" % (
672+
params['src'], params['dest']))
673+
674+
def _same_path(self, src, dest):
675+
if not self.parameters['paths_type'] == 's3s3':
676+
return False
677+
elif src == dest:
678+
return True
679+
elif dest.endswith('/'):
680+
src_base = os.path.basename(src)
681+
return src == os.path.join(dest, src_base)
665682

666683
def _normalize_s3_trailing_slash(self, paths):
667684
for i, path in enumerate(paths):

tests/integration/customizations/s3/test_plugin.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,27 @@ def test_mv_to_nonexistent_bucket(self):
247247
p = aws('s3 mv %s s3://bad-noexist-13143242/foo.txt' % (full_path,))
248248
self.assertEqual(p.rc, 1)
249249

250+
def test_cant_move_file_onto_itself_small_file(self):
251+
# We don't even need a remote file in this case. We can
252+
# immediately validate that we can't move a file onto itself.
253+
bucket_name = self.create_bucket()
254+
self.put_object(bucket_name, key_name='key.txt', contents='foo')
255+
p = aws('s3 mv s3://%s/key.txt s3://%s/key.txt' % (bucket_name, bucket_name))
256+
self.assertEqual(p.rc, 255)
257+
self.assertIn('Cannot mv a file onto itself', p.stderr)
258+
259+
def test_cant_move_large_file_onto_itself(self):
260+
# At the API level, you can multipart copy an object onto itself,
261+
# but a mv command doesn't make sense because a mv is just a
262+
# cp + an rm of the src file. We should be consistent and
263+
# not allow large files to be mv'd onto themselves.
264+
file_contents = six.BytesIO(b'a' * (1024 * 1024 * 10))
265+
bucket_name = self.create_bucket()
266+
self.put_object(bucket_name, key_name='key.txt', contents=file_contents)
267+
p = aws('s3 mv s3://%s/key.txt s3://%s/key.txt' % (bucket_name, bucket_name))
268+
self.assertEqual(p.rc, 255)
269+
self.assertIn('Cannot mv a file onto itself', p.stderr)
270+
250271

251272
class TestRm(BaseS3CLICommand):
252273
@unittest.skipIf(platform.system() not in ['Darwin', 'Linux'],
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/usr/bin/env python
2+
# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"). You
5+
# may not use this file except in compliance with the License. A copy of
6+
# the License is located at
7+
#
8+
# http://aws.amazon.com/apache2.0/
9+
#
10+
# or in the "license" file accompanying this file. This file is
11+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
12+
# ANY KIND, either express or implied. See the License for the specific
13+
# language governing permissions and limitations under the License.
14+
from awscli.testutils import BaseAWSCommandParamsTest, FileCreator
15+
import re
16+
17+
import mock
18+
import six
19+
20+
21+
class TestMvCommand(BaseAWSCommandParamsTest):
22+
23+
prefix = 's3 mv '
24+
25+
def setUp(self):
26+
super(TestMvCommand, self).setUp()
27+
self.files = FileCreator()
28+
29+
def tearDown(self):
30+
super(TestMvCommand, self).tearDown()
31+
self.files.remove_all()
32+
33+
def test_cant_mv_object_onto_itself(self):
34+
cmdline = '%s s3://bucket/key s3://bucket/key' % self.prefix
35+
stderr = self.run_cmd(cmdline, expected_rc=255)[1]
36+
self.assertIn('Cannot mv a file onto itself', stderr)
37+
38+
def test_cant_mv_object_with_implied_name(self):
39+
# The "key" key name is implied in the dst argument.
40+
cmdline = '%s s3://bucket/key s3://bucket/' % self.prefix
41+
stderr = self.run_cmd(cmdline, expected_rc=255)[1]
42+
self.assertIn('Cannot mv a file onto itself', stderr)
43+
44+
45+
if __name__ == "__main__":
46+
unittest.main()

0 commit comments

Comments
 (0)