Merge lp:~verterok/ubuntuone-client/fix-597870 into lp:ubuntuone-client

Proposed by Guillermo Gonzalez
Status: Merged
Approved by: dobey
Approved revision: 555
Merged at revision: 558
Proposed branch: lp:~verterok/ubuntuone-client/fix-597870
Merge into: lp:ubuntuone-client
Diff against target: 162 lines (+108/-5)
2 files modified
tests/syncdaemon/test_fsm.py (+90/-2)
ubuntuone/syncdaemon/filesystem_manager.py (+18/-3)
To merge this branch: bzr merge lp:~verterok/ubuntuone-client/fix-597870
Reviewer Review Type Date Requested Status
dobey (community) Approve
John O'Brien (community) Approve
Review via email: mp+28408@code.launchpad.net

Commit message

Fix fileystem manager _delete_dir_tree method to support read-only shares.

Description of the change

Fix fileystem manager _delete_dir_tree method to support read-only shares.
This will avoid conflicts in read only shares, when a lot of changes are going on and e.g: we get a delete for a node and another node(s) are added to the parent.

To post a comment you must log in.
Revision history for this message
John O'Brien (jdobrien) wrote :

looks good. tests pass

review: Approve
Revision history for this message
dobey (dobey) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'tests/syncdaemon/test_fsm.py'
2--- tests/syncdaemon/test_fsm.py 2010-06-21 20:17:59 +0000
3+++ tests/syncdaemon/test_fsm.py 2010-06-24 13:56:33 +0000
4@@ -2365,7 +2365,7 @@
5
6
7 class SharesTests(FSMTestCase):
8- """ Tests fsm with ro and rw shares. """
9+ """Test fsm with ro and rw shares."""
10
11 def tearDown(self):
12 """ cleanup the mess """
13@@ -2426,6 +2426,95 @@
14 self.fsm.delete_file(testdir)
15 self.assertFalse(os.path.exists(testdir))
16
17+ def test_delete_non_empty_dir_ro_share(self):
18+ """Test that fsm is able to delete a non-empty dir in a ro.share."""
19+ share = self.create_share('ro_share', 'ro_share_name', self.fsm,
20+ self.shares_dir, access_level='View')
21+ testdir = os.path.join(share.path, "path2")
22+ mdid = self.fsm.create(testdir, share.volume_id, is_dir=True)
23+ self.fsm.set_node_id(testdir, "uuid2")
24+ self.fsm.create_partial('uuid2', share.volume_id)
25+ fd = self.fsm.get_partial_for_writing('uuid2', share.volume_id)
26+ fd.flush()
27+ fd.close()
28+ self.fsm.remove_partial('uuid2', share.volume_id)
29+ self.fsm.upload_finished(mdid, self.fsm.get_by_mdid(mdid).local_hash)
30+ # crete a file inside the testdir
31+ testfile = os.path.join(testdir, "a_file")
32+ mdid = self.fsm.create(testfile, share.volume_id, is_dir=False)
33+ self.fsm.set_node_id(testfile, "uuid3")
34+ self.fsm.create_partial('uuid3', share.volume_id)
35+ fd = self.fsm.get_partial_for_writing('uuid3', share.volume_id)
36+ fd.flush()
37+ fd.close()
38+ self.fsm.commit_partial('uuid3', share.volume_id, None)
39+ self.fsm.upload_finished(mdid, self.fsm.get_by_mdid(mdid).local_hash)
40+ self.assertTrue(os.path.exists(testdir))
41+ self.assertTrue(os.path.exists(testfile))
42+ self.fsm.delete_file(testdir)
43+ self.assertFalse(os.path.exists(testdir))
44+ self.assertFalse(os.path.exists(testfile))
45+
46+ def test_delete_non_empty_dir_rw_share(self):
47+ """Test that fsm is able to delete a non-empty dir in a rw.share."""
48+ share = self.create_share('rw_share', 'rw_share_name', self.fsm,
49+ self.shares_dir, access_level='Modify')
50+ testdir = os.path.join(share.path, "path2")
51+ mdid = self.fsm.create(testdir, share.volume_id, is_dir=True)
52+ self.fsm.set_node_id(testdir, "uuid2")
53+ self.fsm.create_partial('uuid2', share.volume_id)
54+ fd = self.fsm.get_partial_for_writing('uuid2', share.volume_id)
55+ fd.flush()
56+ fd.close()
57+ self.fsm.remove_partial('uuid2', share.volume_id)
58+ self.fsm.upload_finished(mdid, self.fsm.get_by_mdid(mdid).local_hash)
59+ # crete a file inside the testdir
60+ testfile = os.path.join(testdir, "a_file")
61+ mdid = self.fsm.create(testfile, share.volume_id, is_dir=False)
62+ self.fsm.set_node_id(testfile, "uuid3")
63+ self.fsm.create_partial('uuid3', share.volume_id)
64+ fd = self.fsm.get_partial_for_writing('uuid3', share.volume_id)
65+ fd.flush()
66+ fd.close()
67+ self.fsm.commit_partial('uuid3', share.volume_id, None)
68+ self.fsm.upload_finished(mdid, self.fsm.get_by_mdid(mdid).local_hash)
69+ self.assertTrue(os.path.exists(testdir))
70+ self.assertTrue(os.path.exists(testfile))
71+ self.fsm.delete_file(testdir)
72+ self.assertFalse(os.path.exists(testdir))
73+ self.assertFalse(os.path.exists(testfile))
74+
75+ def test_delete_non_empty_dir_bad_perms_rw_share(self):
76+ """Test that fsm is able to delete a non-empty dir in a rw.share."""
77+ share = self.create_share('rw_share', 'rw_share_name', self.fsm,
78+ self.shares_dir, access_level='Modify')
79+ testdir = os.path.join(share.path, "path2")
80+ mdid = self.fsm.create(testdir, share.volume_id, is_dir=True)
81+ self.fsm.set_node_id(testdir, "uuid2")
82+ self.fsm.create_partial('uuid2', share.volume_id)
83+ fd = self.fsm.get_partial_for_writing('uuid2', share.volume_id)
84+ fd.flush()
85+ fd.close()
86+ self.fsm.remove_partial('uuid2', share.volume_id)
87+ self.fsm.upload_finished(mdid, self.fsm.get_by_mdid(mdid).local_hash)
88+ # crete a file inside the testdir
89+ testfile = os.path.join(testdir, "a_file")
90+ mdid = self.fsm.create(testfile, share.volume_id, is_dir=False)
91+ self.fsm.set_node_id(testfile, "uuid3")
92+ self.fsm.create_partial('uuid3', share.volume_id)
93+ fd = self.fsm.get_partial_for_writing('uuid3', share.volume_id)
94+ fd.flush()
95+ fd.close()
96+ self.fsm.commit_partial('uuid3', share.volume_id, None)
97+ self.fsm.upload_finished(mdid, self.fsm.get_by_mdid(mdid).local_hash)
98+ self.assertTrue(os.path.exists(testdir))
99+ self.assertTrue(os.path.exists(testfile))
100+ # make the dir read-only
101+ os.chmod(testdir, 0555)
102+ self.assertRaises(OSError, self.fsm.delete_file, testdir)
103+ self.assertTrue(os.path.exists(testdir))
104+ self.assertTrue(os.path.exists(testfile))
105+
106 def test_delete_file_ro_share(self):
107 """ Test that fsm is able to delete a file in a ro-share. """
108 self.share = self.create_share('ro_share', 'ro_share_name', self.fsm,
109@@ -2493,7 +2582,6 @@
110 self.fsm.commit_partial('uuid3', self.share.volume_id, None)
111 self.assertTrue(os.path.exists(testfile))
112
113-
114 def test_share_and_root(self):
115 """ Test the creation of a file with the same relative path in a share
116 and in the root.
117
118=== modified file 'ubuntuone/syncdaemon/filesystem_manager.py'
119--- ubuntuone/syncdaemon/filesystem_manager.py 2010-06-21 20:17:59 +0000
120+++ ubuntuone/syncdaemon/filesystem_manager.py 2010-06-24 13:56:33 +0000
121@@ -724,7 +724,7 @@
122 del self._idx_node_id[(mdobj["share_id"], mdobj["node_id"])]
123 del self.fs[mdid]
124
125- def _delete_dir_tree(self, path):
126+ def _delete_dir_tree(self, path, share_id):
127 """Helper function to recursively remove a non-empty directory.
128
129 Removes the dir if there aren't local changes, and returns True.
130@@ -753,7 +753,21 @@
131 filter_name = is_dir and "FS_DIR_DELETE" or "FS_FILE_DELETE"
132 self.eq.add_to_mute_filter(filter_name, p)
133 self.delete_metadata(p)
134- shutil.rmtree(path)
135+
136+ # get the share and if it's read-only
137+ share = self._get_share(share_id)
138+ is_ro_share = not share.can_write()
139+ def handle_ro_share(func, subpath, exc_info):
140+ """Handle rmtree EACCES error for ro-shares."""
141+ if exc_info[0] == OSError and exc_info[1].errno == errno.EACCES \
142+ and is_ro_share:
143+ # use EnableShareWrite directly to avoid a lot of share
144+ # lookups.
145+ with EnableShareWrite(share, subpath):
146+ func(subpath)
147+ else:
148+ raise
149+ shutil.rmtree(path, onerror=handle_ro_share)
150 return True
151
152 def delete_file(self, path):
153@@ -778,7 +792,8 @@
154 except OSError, e:
155 self.eq.rm_from_mute_filter(expected_event, path)
156 if e.errno == errno.ENOTEMPTY:
157- if not self._delete_dir_tree(path=path):
158+ if not self._delete_dir_tree(path=path,
159+ share_id=mdobj['share_id']):
160 raise
161 else:
162 m = "OSError %s when trying to remove file/dir %r"

Subscribers

People subscribed via source and target branches