Merge lp:~mandel/ubuntuone-client/illegal_windows_chars into lp:ubuntuone-client

Proposed by Manuel de la Peña
Status: Merged
Approved by: Manuel de la Peña
Approved revision: 1040
Merged at revision: 1007
Proposed branch: lp:~mandel/ubuntuone-client/illegal_windows_chars
Merge into: lp:ubuntuone-client
Diff against target: 763 lines (+377/-150)
3 files modified
tests/platform/windows/test_os_helper.py (+189/-100)
ubuntuone/platform/windows/ipc.py (+2/-3)
ubuntuone/platform/windows/os_helper.py (+186/-47)
To merge this branch: bzr merge lp:~mandel/ubuntuone-client/illegal_windows_chars
Reviewer Review Type Date Requested Status
Natalia Bidart (community) Approve
Alejandro J. Cura (community) Approve
Review via email: mp+65195@code.launchpad.net

Commit message

Fixes: lp:799707 and lp:799722

This branches fixes two different bugs:

1. The correct credentials management tool is used in the windows IPC.
2. Allowed to use illegal chars to be used in the os_helper so that they can be used in the local file system.

Description of the change

This branches fixes two different bugs:

1. The correct credentials management tool is used in the windows IPC.
2. Allowed to use illegal chars to be used in the os_helper so that they can be used in the local file system. In order to be able to get this branch working pywin32 has to be patch (https://sourceforge.net/tracker/index.php?func=detail&aid=3323058&group_id=78018&atid=551954#) with the following:

diff -r 7dce71d174a9 win32/src/win32security.i
--- a/win32/src/win32security.i Sat Jun 18 10:16:06 2011 -0400
+++ b/win32/src/win32security.i Mon Jun 20 14:15:27 2011 +0200
@@ -2108,7 +2108,7 @@
  if (!PyWinObject_AsTCHAR(obFname, &fname))
   goto done;

- if (GetFileSecurity(fname, info, psd, dwSize, &dwSize)) {
+ if (GetFileSecurityW(fname, info, psd, dwSize, &dwSize)) {
   PyErr_SetString(PyExc_RuntimeError, "Can't query for SECURITY_DESCRIPTOR size info?");
   goto done;
  }
@@ -2117,7 +2117,7 @@
   PyErr_SetString(PyExc_MemoryError, "allocating SECURITY_DESCRIPTOR");
   goto done;
  }
- if (!GetFileSecurity(fname, info, psd, dwSize, &dwSize)) {
+ if (!GetFileSecurityW(fname, info, psd, dwSize, &dwSize)) {
   PyWin_SetAPIError("GetFileSecurity");
   goto done;
  }
@@ -2153,7 +2153,7 @@
  PSECURITY_DESCRIPTOR psd;
  if (!PyWinObject_AsSECURITY_DESCRIPTOR(obsd, &psd))
   goto done;
- if (!SetFileSecurity(fname, info, psd)) {
+ if (!SetFileSecurityW(fname, info, psd)) {
   PyWin_SetAPIError("SetFileSecurity");
   goto done;
  }

Once pywin32 has been patched in your system please run the following test on windows:

python C:\Python27\Scripts u1trial tests\platform\test_os_helper.py
python C:\Python27\Scripts u1trial tests\platform\windows\test_os_helper.py
python C:\Python27\Scripts u1trial tests\syncdaemon\test_vm.py

The changes in the code should have not added too much overhead to the IO operations meaning that the test_vm tests should not timeout.

To post a comment you must log in.
Revision history for this message
Natalia Bidart (nataliabidart) wrote :

* Can you please replace:
        for char in WINDOWS_ILLEGAL_CHARS_MAP:
            self.testfile = self.testfile + char
with:
    self.testfile = ''.join(WINDOWS_ILLEGAL_CHARS_MAP)

* typos: "illechal", "illegla"

* can you please use os.path.splitdrive(path) (http://docs.python.org/library/os.path.html#os.path.splitdrive) instead of the custom logic in diff line 392 (and everywhere else where you split the volume letter)?

* can you replace:

    for illegal_char in WINDOWS_ILLEGAL_CHARS_MAP:
        if illegal_char in path:
            return True

with:

    any(c in WINDOWS_ILLEGAL_CHARS_MAP for c in path)

* can you please replace:

        for illegal_char in WINDOWS_ILLEGAL_CHARS_MAP:
            partial = partial.replace(illegal_char,
                                      WINDOWS_ILLEGAL_CHARS_MAP[illegal_char])

with:

       for illegal_char, replacement in WINDOWS_ILLEGAL_CHARS_MAP.iteritems():
            partial = partial.replace(illegal_char, replacement)

* can you please add a logger.exception statement inside the except OSError, e: within set_no_rights? it will be useful to diagnose potential issues from log reports from our users. Same for remove_file, remove_dir, make_dir, open_file, rename, stat_path.

* why are you doing this in abspath? path = path.replace('/', '\\') Seems unnecessary and may hide bugs in our code regarding multiplatform path management.

review: Needs Fixing
Revision history for this message
Alejandro J. Cura (alecu) wrote :

"if e.winerror and e.winerror == 123" should be formulated as """if getattr(e, "winerror", None) == 123""", in case the exception has no winerror attribute. And it should perhaps be moved into a function called let's say "checkWinError123(e)", since it appears at least 8 times through the branch.

calling set_no_rights from inside set_no_rights (in the case of errors) risks the possibility of entering an infinite recursion loop; I suggest moving the three lines inside the try to a small new function, and calling it twice, once from the try, and once from the "if e.winerror...".

typos:
 * "illechal" -> "illegal"
 * "there reason" -> "the reason"
 * spurious spaces have appeared at the end of lines 487 and 496 of the diff
 * "illegla" -> "illegal"

review: Needs Fixing
Revision history for this message
Manuel de la Peña (mandel) wrote :

> * Can you please replace:
> for char in WINDOWS_ILLEGAL_CHARS_MAP:
> self.testfile = self.testfile + char
> with:
> self.testfile = ''.join(WINDOWS_ILLEGAL_CHARS_MAP)
>
> * typos: "illechal", "illegla"
>
> * can you please use os.path.splitdrive(path)
> (http://docs.python.org/library/os.path.html#os.path.splitdrive) instead of
> the custom logic in diff line 392 (and everywhere else where you split the
> volume letter)?
>
> * can you replace:
>
> for illegal_char in WINDOWS_ILLEGAL_CHARS_MAP:
> if illegal_char in path:
> return True
>
> with:
>
> any(c in WINDOWS_ILLEGAL_CHARS_MAP for c in path)
>
> * can you please replace:
>
> for illegal_char in WINDOWS_ILLEGAL_CHARS_MAP:
> partial = partial.replace(illegal_char,
> WINDOWS_ILLEGAL_CHARS_MAP[illegal_char])
>
> with:
>
> for illegal_char, replacement in WINDOWS_ILLEGAL_CHARS_MAP.iteritems():
> partial = partial.replace(illegal_char, replacement)
>
> * can you please add a logger.exception statement inside the except OSError,
> e: within set_no_rights? it will be useful to diagnose potential issues from
> log reports from our users. Same for remove_file, remove_dir, make_dir,
> open_file, rename, stat_path.
>

All the above are fixed.

> * why are you doing this in abspath? path = path.replace('/', '\\') Seems
> unnecessary and may hide bugs in our code regarding multiplatform path
> management.

I have left it because if not there is a vm test that fails... the test uses / for the paths, and while we return the correct path, the assertion fails because on windows we use \\. Since verterok is not here I have left it there with a #TODO asking if it is needed.

Revision history for this message
Alejandro J. Cura (alecu) wrote :

there are still some instances of the old == 123 check, lines 459 and 638 in the diff

review: Needs Fixing
Revision history for this message
Natalia Bidart (nataliabidart) wrote :

* Unless I'm missing something, seems like the custom logic to separate the drive letter has not been replaced by os.path.splitdrive.

* All the logger.exception calls should not add the exception itself to the message string since is automatically added by the logger. So, a sentence like this:

        logger.exception('Goet exception %s when setting path %s to no rights',
                         e, path)

should be:

        logger.exception('Got exception when setting path %s to no rights:', path)

* As Alejandro mentioned, there are some new trailing spaces added by this branch, please remove that.

review: Needs Fixing
Revision history for this message
Alejandro J. Cura (alecu) wrote :

Just a few typos:

"Set the rigths" -> "Set the rights"
            ^

"Get exception when triying" -> "Got exception when trying"
  ^ ^

'Got exception when trun to remove path %s'
                      ^ ???

'Got exception when reyting to remove dir %s'
                      ^ retrying?

"TODO: o we really" -> "TODO: do we really"
       ^

review: Approve
Revision history for this message
Natalia Bidart (nataliabidart) wrote :

Looks good now!

review: Approve
Revision history for this message
Ubuntu One Auto Pilot (otto-pilot) wrote :
Download full text (35.1 KiB)

The attempt to merge lp:~mandel/ubuntuone-client/illegal_windows_chars into lp:ubuntuone-client failed. Below is the output from the failed tests.

/usr/bin/gnome-autogen.sh
checking for autoconf >= 2.53...
  testing autoconf2.50... not found.
  testing autoconf... found 2.67
checking for automake >= 1.10...
  testing automake-1.11... found 1.11.1
checking for libtool >= 1.5...
  testing libtoolize... found 2.2.6b
checking for intltool >= 0.30...
  testing intltoolize... found 0.41.1
checking for pkg-config >= 0.14.0...
  testing pkg-config... found 0.25
checking for gtk-doc >= 1.0...
  testing gtkdocize... found 1.17
Checking for required M4 macros...
Checking for forbidden M4 macros...
Processing ./configure.ac
Running libtoolize...
libtoolize: putting auxiliary files in `.'.
libtoolize: copying file `./ltmain.sh'
libtoolize: putting macros in AC_CONFIG_MACRO_DIR, `m4'.
libtoolize: copying file `m4/libtool.m4'
libtoolize: copying file `m4/ltoptions.m4'
libtoolize: copying file `m4/ltsugar.m4'
libtoolize: copying file `m4/ltversion.m4'
libtoolize: copying file `m4/lt~obsolete.m4'
Running intltoolize...
Running gtkdocize...
Running aclocal-1.11...
Running autoconf...
Running autoheader...
Running automake-1.11...
Running ./configure --enable-gtk-doc --enable-debug ...
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /bin/mkdir -p
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking for style of include used by make... GNU
checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking dependency style of gcc... gcc3
checking for library containing strerror... none required
checking for gcc... (cached) gcc
checking whether we are using the GNU C compiler... (cached) yes
checking whether gcc accepts -g... (cached) yes
checking for gcc option to accept ISO C89... (cached) none needed
checking dependency style of gcc... (cached) gcc3
checking build system type... i686-pc-linux-gnu
checking host system type... i686-pc-linux-gnu
checking for a sed that does not truncate output... /bin/sed
checking for grep that handles long lines and -e... /bin/grep
checking for egrep... /bin/grep -E
checking for fgrep... /bin/grep -F
checking for ld used by gcc... /usr/bin/ld
checking if the linker (/usr/bin/ld) is GNU ld... yes
checking for BSD- or MS-compatible name lister (nm)... /usr/bin/nm -B
checking the name lister (/usr/bin/nm -B) interface... BSD nm
checking whether ln -s works... yes
checking the maximum length of command line arguments... 1572864
checking whether the shell understands some XSI constructs... yes
checking whether the shell understands "+="... yes
checking for /usr/bin/ld option to reload object files... -r
checking for objdump... objdump
checking how to recognize dependent...

Revision history for this message
Ubuntu One Auto Pilot (otto-pilot) wrote :

There are additional revisions which have not been approved in review. Please seek review and approval of these new revisions.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'tests/platform/windows/test_os_helper.py'
2--- tests/platform/windows/test_os_helper.py 2011-02-07 15:34:55 +0000
3+++ tests/platform/windows/test_os_helper.py 2011-06-21 00:52:37 +0000
4@@ -17,115 +17,204 @@
5 """Specific tests for the os_helper on Windows."""
6
7 import os
8-
9-from win32api import GetUserName
10-
11-from ntsecuritycon import (
12- FILE_ALL_ACCESS,
13- FILE_GENERIC_READ,
14- FILE_GENERIC_WRITE,
15-)
16+import errno
17+
18 from ubuntuone.platform.windows.os_helper import (
19- _set_file_attributes,
20+ _remove_windows_illegal_chars,
21 access,
22- set_no_rights
23+ listdir,
24+ make_dir,
25+ open_file,
26+ path_exists,
27+ remove_dir,
28+ remove_file,
29+ rename,
30+ set_no_rights,
31+ set_file_readwrite,
32+ stat_path,
33+ WINDOWS_ILLEGAL_CHARS_MAP
34 )
35 from contrib.testing.testcase import BaseTwistedTestCase
36
37-class OSWrapperTests(BaseTwistedTestCase):
38- """Test specific windows implementation details."""
39+
40+class TestIllegalPaths(BaseTwistedTestCase):
41+ """Test all the operations using illegal paths."""
42
43 def setUp(self):
44 """Setup for the tests."""
45- super(OSWrapperTests, self).setUp()
46+ super(TestIllegalPaths, self).setUp()
47 self.basedir = self.mktemp('test_root')
48 self.testfile = os.path.join(self.basedir, "test_file")
49- self.fd = open(self.testfile, 'w')
50-
51- def tearDown(self):
52- """Clean the env for the next test."""
53- self.fd.close();
54- self.rmtree(self.basedir)
55- super(OSWrapperTests, self).tearDown()
56-
57- def test_access_no_rights(self):
58- """Test when the sid is not present."""
59- # remove all the rights from the test file so that
60- # we cannot read or write
61+ self.testfile += ''.join(WINDOWS_ILLEGAL_CHARS_MAP)
62+ self.fd = open(_remove_windows_illegal_chars(self.testfile), 'w')
63+
64+ def test_path_file_exist_yes(self):
65+ """Test that the file exists."""
66+ self.assertTrue(path_exists(self.testfile))
67+
68+ def test_path_file_exist_no(self):
69+ """Test that the file doesn't exist."""
70+ self.fd.close()
71+ os.remove(_remove_windows_illegal_chars(self.testfile))
72+ self.assertFalse(path_exists(self.testfile))
73+
74+ def test_path_dir_exist_yes(self):
75+ """Test that the dir exists."""
76+ self.assertTrue(path_exists(self.basedir))
77+
78+ def test_path_dir_exist_no(self):
79+ """Test that the dir doesn't exist."""
80+ self.fd.close()
81+ os.remove(_remove_windows_illegal_chars(self.testfile))
82+ self.assertFalse(path_exists(os.path.join(self.basedir, 'nodir')))
83+
84+ def test_remove_file(self):
85+ """Test the remove file."""
86+ self.fd.close()
87+ remove_file(self.testfile)
88+ self.assertFalse(os.path.exists(
89+ _remove_windows_illegal_chars(self.testfile)))
90+
91+ def test_remove_dir(self):
92+ """Test the remove dir."""
93+ testdir = os.path.join(self.basedir, 'foodir')
94+ os.mkdir(testdir)
95+ assert os.path.exists(testdir)
96+ remove_dir(testdir)
97+ self.assertFalse(path_exists(testdir))
98+
99+ def test_make_dir_one(self):
100+ """Test the make dir with one dir."""
101+ testdir = os.path.join(self.basedir, 'foodir')
102+ assert not os.path.exists(testdir)
103+ make_dir(testdir)
104+ self.assertTrue(os.path.exists(testdir))
105+
106+ def test_make_dir_already_there(self):
107+ """Test the make dir with one dir that exists."""
108+ self.assertRaises(OSError, make_dir, self.basedir)
109+
110+ def test_make_dir_recursive_no(self):
111+ """Test the make dir with some dirs, not recursive explicit."""
112+ testdir = os.path.join(self.basedir, 'foo', 'bar')
113+ assert not os.path.exists(testdir)
114+ self.assertRaises(OSError, make_dir, testdir)
115+
116+ def test_make_dir_recursive_yes(self):
117+ """Test the make dir with some dirs, recursive."""
118+ testdir = os.path.join(self.basedir, 'foo', 'bar')
119+ assert not os.path.exists(testdir)
120+ make_dir(testdir, recursive=True)
121+ self.assertTrue(os.path.exists(testdir))
122+
123+ def test_open_file_not_there(self):
124+ """Open a file that does not exist."""
125+ self.assertRaises(IOError, open_file, os.path.join(self.basedir, 'no'))
126+
127+ def test_open_file_gets_a_fileobject(self):
128+ """Open a file, and get a file object."""
129+ testfile = os.path.join(self.basedir, 'testfile')
130+ open(testfile, 'w').close()
131+ f = open_file(testfile)
132+ self.assertTrue(isinstance(f, file))
133+
134+ def test_open_file_read(self):
135+ """Open a file, and read."""
136+ testfile = os.path.join(self.basedir, 'testfile')
137+ with open(testfile, 'w') as fh:
138+ fh.write("foo")
139+ f = open_file(testfile, 'r')
140+ self.assertTrue(f.read(), "foo")
141+
142+ def test_open_file_write(self):
143+ """Open a file, and write."""
144+ testfile = os.path.join(self.basedir, 'testfile')
145+ with open_file(testfile, 'w') as fh:
146+ fh.write("foo")
147+ f = open(testfile)
148+ self.assertTrue(f.read(), "foo")
149+
150+ def test_rename_not_there(self):
151+ """Rename something that does not exist."""
152+ self.assertRaises(OSError, rename,
153+ os.path.join(self.basedir, 'no>'), 'foo')
154+
155+ def test_rename_file(self):
156+ """Rename a file."""
157+ testfile1 = os.path.join(self.basedir, 'testfile1>')
158+ testfile2 = os.path.join(self.basedir, 'testfile2<')
159+ open(_remove_windows_illegal_chars(testfile1), 'w').close()
160+ rename(testfile1, testfile2)
161+ self.assertFalse(os.path.exists(
162+ _remove_windows_illegal_chars(testfile1)))
163+ self.assertTrue(os.path.exists(
164+ _remove_windows_illegal_chars(testfile2)))
165+
166+ def test_rename_dir(self):
167+ """Rename a dir."""
168+ testdir1 = os.path.join(self.basedir, 'testdir1>')
169+ testdir2 = os.path.join(self.basedir, 'testdir2<')
170+ os.mkdir(_remove_windows_illegal_chars(testdir1))
171+ rename(testdir1, testdir2)
172+ self.assertFalse(os.path.exists(
173+ _remove_windows_illegal_chars(testdir1)))
174+ self.assertTrue(os.path.exists(
175+ _remove_windows_illegal_chars(testdir2)))
176+
177+ def test_listdir(self):
178+ """Return a list of the files in a dir."""
179+ open(os.path.join(self.basedir, 'foo'), 'w').close()
180+ open(os.path.join(self.basedir, 'bar'), 'w').close()
181+ l = listdir(self.basedir)
182+ test_file = u'test_file\u200b\u2033\u200b\u200b\u203d\u200b\u200b' +\
183+ u'\u26b9\u200b\u200b\u2223\u200b\u200b\u2236\u200b\u200b' +\
184+ u'\u2039\u200b\u200b\u2044\u200b\u200b\u203a\u200b'
185+ self.assertEqual(sorted(l), ['bar', 'foo', test_file])
186+
187+ def test_access_rw(self):
188+ """Test access on a file with full permission."""
189+ self.assertTrue(access(self.testfile))
190+
191+ def test_access_ro(self):
192+ """Test access on a file with read only permission."""
193+ fixed_path = _remove_windows_illegal_chars(self.testfile)
194+ os.chmod(fixed_path, 0o444)
195+ self.assertTrue(access(self.testfile))
196+ os.chmod(fixed_path, 0o664)
197+
198+ def test_access_nothing(self):
199+ """Test access on a file with no permission at all."""
200 set_no_rights(self.testfile)
201 self.assertFalse(access(self.testfile))
202-
203- def test_access_read_write_user(self):
204- """Test when the user sid has rw rights."""
205- # set the file to be read and write just by the user
206- groups = {}
207- groups[GetUserName()] = FILE_GENERIC_READ | FILE_GENERIC_WRITE
208- _set_file_attributes(self.testfile, groups)
209- self.assertTrue(access(self.testfile))
210-
211- def test_access_read_write_everyone(self):
212- """Test when the everyone sid has rw rights."""
213- groups = {}
214- groups['Everyone'] = FILE_GENERIC_READ | FILE_GENERIC_WRITE
215- _set_file_attributes(self.testfile, groups)
216- self.assertTrue(access(self.testfile))
217-
218- def test_access_write_user_everyone_read(self):
219- """Test when the user sid has w rights."""
220- groups = {}
221- groups[GetUserName()] = FILE_GENERIC_WRITE
222- groups['Everyone'] = FILE_GENERIC_READ
223- _set_file_attributes(self.testfile, groups)
224- self.assertTrue(access(self.testfile))
225-
226- def test_access_write_everyone_user_read(self):
227- """Test when the everyone sid has w rights"""
228- groups = {}
229- groups[GetUserName()] = FILE_GENERIC_READ
230- groups['Everyone'] = FILE_GENERIC_WRITE
231- _set_file_attributes(self.testfile, groups)
232- self.assertTrue(access(self.testfile))
233-
234- def test_access_write_user_everyone(self):
235- """Test when everyone and user have w rights."""
236- groups = {}
237- groups[GetUserName()] = FILE_GENERIC_WRITE
238- groups['Everyone'] = FILE_GENERIC_WRITE
239- _set_file_attributes(self.testfile, groups)
240- self.assertFalse(access(self.testfile))
241-
242- def test_access_read_user(self):
243- """Test when the sid has r rights."""
244- groups = {}
245- groups[GetUserName()] = FILE_GENERIC_READ
246- _set_file_attributes(self.testfile, groups)
247- self.assertTrue(access(self.testfile))
248-
249- def test_access_read_everyone(self):
250- """Test when everyone has r rights."""
251- groups = {}
252- groups['Everyone'] = FILE_GENERIC_READ
253- _set_file_attributes(self.testfile, groups)
254- self.assertTrue(access(self.testfile))
255-
256- def test_access_read_user_everyone(self):
257- """Test when user and everyone have r rights."""
258- groups = {}
259- groups[GetUserName()] = FILE_GENERIC_READ
260- groups['Everyone'] = FILE_GENERIC_READ
261- _set_file_attributes(self.testfile, groups)
262- self.assertTrue(access(self.testfile))
263-
264- def test_access_full_user(self):
265- """Test when the sid has full control."""
266- groups = {}
267- groups[GetUserName()] = FILE_ALL_ACCESS
268- _set_file_attributes(self.testfile, groups)
269- self.assertTrue(access(self.testfile))
270-
271- def test_access_full_everyone(self):
272- """test when everyone has full control."""
273- groups = {}
274- groups['Everyone'] = FILE_ALL_ACCESS
275- _set_file_attributes(self.testfile, groups)
276- self.assertTrue(access(self.testfile))
277+ set_file_readwrite(self.testfile)
278+
279+ def test_stat_normal(self):
280+ """Test on a normal file."""
281+ self.assertEqual(os.stat(_remove_windows_illegal_chars(self.testfile)),
282+ stat_path(self.testfile))
283+
284+ def test_stat_no_path(self):
285+ """Test that it raises proper error when no file is there."""
286+ try:
287+ return stat_path(os.path.join(self.basedir, 'nofile'))
288+ except OSError, e:
289+ self.assertEqual(e.errno, errno.ENOENT)
290+
291+ def test_path_exists_file_yes(self):
292+ """The file is there."""
293+ self.assertTrue(path_exists(self.testfile))
294+
295+ def test_path_exists_file_no(self):
296+ """The file is not there."""
297+ self.fd.close()
298+ os.remove(_remove_windows_illegal_chars(self.testfile))
299+ self.assertFalse(path_exists(self.testfile))
300+
301+ def test_path_exists_dir_yes(self):
302+ """The dir is there."""
303+ self.assertTrue(path_exists(self.basedir))
304+
305+ def test_path_exists_dir_no(self):
306+ """The dir is not there."""
307+ self.fd.close()
308+ self.assertFalse(path_exists(os.path.join(self.basedir, 'subdir')))
309\ No newline at end of file
310
311=== modified file 'ubuntuone/platform/windows/ipc.py'
312--- ubuntuone/platform/windows/ipc.py 2011-06-15 14:46:04 +0000
313+++ ubuntuone/platform/windows/ipc.py 2011-06-21 00:52:37 +0000
314@@ -58,8 +58,7 @@
315 from twisted.spread.pb import Referenceable, Root, PBServerFactory
316 from twisted.python.failure import Failure
317 from ubuntuone.syncdaemon.interfaces import IMarker
318-from ubuntuone.credentials import CredentialsManagementTool
319-from ubuntuone.platform import get_creds_proxy
320+from ubuntuone.platform.windows import CredentialsManagementTool
321 from ubuntuone.platform.windows.network_manager import NetworkManager
322 from ubuntuone.syncdaemon.interaction_interfaces import (
323 bool_str,
324@@ -1243,7 +1242,7 @@
325
326 def _register_to_signals(self):
327 """Register to the creds signals."""
328- self.creds = get_creds_proxy()
329+ self.creds = CredentialsManagementTool()
330 self.creds.register_to_credentials_found(self.on_credentials_found_cb)
331 self.creds.register_to_credentials_not_found(
332 self.on_credentials_error_cb)
333
334=== modified file 'ubuntuone/platform/windows/os_helper.py'
335--- ubuntuone/platform/windows/os_helper.py 2011-06-17 09:31:15 +0000
336+++ ubuntuone/platform/windows/os_helper.py 2011-06-21 00:52:37 +0000
337@@ -26,8 +26,8 @@
338 from functools import wraps
339 from contextlib import contextmanager
340 from pywintypes import error as PyWinError
341-from win32api import MoveFileEx, GetUserName, GetFileAttributes
342-from win32file import FindFilesW
343+from win32api import GetUserName, GetFileAttributes
344+from win32file import FindFilesW, MoveFileExW
345 from win32com.client import Dispatch
346 from pywintypes import error
347 from win32con import FILE_ATTRIBUTE_READONLY
348@@ -58,11 +58,36 @@
349 if sys.platform != 'win32':
350 WindowsError = None
351
352+
353+logger = logging.getLogger('ubuntuone.SyncDaemon.VM')
354 platform = 'win32'
355
356 LONG_PATH_PREFIX = '\\\\?\\'
357 EVERYONE_GROUP = 'Everyone'
358 ADMINISTRATORS_GROUP = 'Administrators'
359+# a map that contains the illegal chars and their 'utf8' representation on
360+# windows so that we can show something to the user
361+WINDOWS_ILLEGAL_CHARS_MAP = {
362+ '<':u'\N{ZERO WIDTH SPACE}\N{SINGLE LEFT-POINTING ANGLE QUOTATION MARK}\N{ZERO WIDTH SPACE}',
363+ '>':u'\N{ZERO WIDTH SPACE}\N{SINGLE RIGHT-POINTING ANGLE QUOTATION MARK}\N{ZERO WIDTH SPACE}',
364+ ':':u'\N{ZERO WIDTH SPACE}\N{RATIO}\N{ZERO WIDTH SPACE}',
365+ '"':u'\N{ZERO WIDTH SPACE}\N{DOUBLE PRIME}\N{ZERO WIDTH SPACE}',
366+ '/':u'\N{ZERO WIDTH SPACE}\N{FRACTION SLASH}\N{ZERO WIDTH SPACE}',
367+ '|':u'\N{ZERO WIDTH SPACE}\N{DIVIDES}\N{ZERO WIDTH SPACE}',
368+ '?':u'\N{ZERO WIDTH SPACE}\N{INTERROBANG}\N{ZERO WIDTH SPACE}',
369+ '*':u'\N{ZERO WIDTH SPACE}\N{SEXTILE}\N{ZERO WIDTH SPACE}'
370+}
371+LINUX_ILLEGAL_CHARS_MAP = {
372+ u'\N{ZERO WIDTH SPACE}\N{SINGLE LEFT-POINTING ANGLE QUOTATION MARK}\N{ZERO WIDTH SPACE}':'<',
373+ u'\N{ZERO WIDTH SPACE}\N{SINGLE RIGHT-POINTING ANGLE QUOTATION MARK}\N{ZERO WIDTH SPACE}':'>',
374+ u'\N{ZERO WIDTH SPACE}\N{RATIO}\N{ZERO WIDTH SPACE}':':',
375+ u'\N{ZERO WIDTH SPACE}\N{DOUBLE PRIME}\N{ZERO WIDTH SPACE}':'"',
376+ u'\N{ZERO WIDTH SPACE}\N{FRACTION SLASH}\N{ZERO WIDTH SPACE}':'/',
377+ u'\N{ZERO WIDTH SPACE}\N{SET MINUS}\N{ZERO WIDTH SPACE}':'\\',
378+ u'\N{ZERO WIDTH SPACE}\N{DIVIDES}\N{ZERO WIDTH SPACE}':'|',
379+ u'\N{ZERO WIDTH SPACE}\N{INTERROBANG}\N{ZERO WIDTH SPACE}':'?',
380+ u'\N{ZERO WIDTH SPACE}\N{SEXTILE}\N{ZERO WIDTH SPACE}':'*'
381+}
382
383
384 def _int_to_bin(n):
385@@ -83,9 +108,79 @@
386 return LookupAccountName('', group_name)[0]
387
388
389+def _has_read_mask(number):
390+ """Return if the read flag is present."""
391+ # get the bin representation of the mask
392+ binary = _int_to_bin(number)
393+ # there is actual no documentation of this in MSDN but if bt 28 is set,
394+ # the mask has full access, more info can be found here:
395+ # http://www.iu.hio.no/cfengine/docs/cfengine-NT/node47.html
396+ if binary[28] == '1':
397+ return True
398+ # there is no documentation in MSDN about this, but if bit 0 and 3 are true
399+ # we have the read flag, more info can be found here:
400+ # http://www.iu.hio.no/cfengine/docs/cfengine-NT/node47.html
401+ return binary[0] == '1' and binary[3] == '1'
402+
403+
404+def _check_winerror_123(exception, cb, path, *args, **kwargs):
405+ """Checks if an exception is a 123 error and executes the cb."""
406+ if getattr(exception, "winerror", None) == 123 :
407+ path = _remove_windows_illegal_chars(path)
408+ return cb(path, *args, **kwargs)
409+ else:
410+ raise exception
411+
412+
413+def _is_illegal_path(path):
414+ """Return if the path is illegal in the system."""
415+ if LONG_PATH_PREFIX in path:
416+ path = path.replace(LONG_PATH_PREFIX, '')
417+ return any(c in WINDOWS_ILLEGAL_CHARS_MAP
418+ for c in os.path.splitdrive(path)[1])
419+
420+
421+def _remove_windows_illegal_chars(path):
422+ """Remove illegal chars and return a valid windows path."""
423+ if not _is_illegal_path(path):
424+ return path
425+ prefix = False
426+ # do not split the path if you have a long path prefix since the
427+ # partial paths will be wrong.
428+ if LONG_PATH_PREFIX in path:
429+ # remove it and remember to add it
430+ path = path.replace(LONG_PATH_PREFIX, '')
431+ prefix = True
432+ volume, path = os.path.splitdrive(path)
433+ partial_paths = path.split('\\')
434+ result = []
435+ for partial in partial_paths:
436+ # ensure that the illegal chars are not there
437+ for illegal_char, replacement in WINDOWS_ILLEGAL_CHARS_MAP.iteritems():
438+ partial = partial.replace(illegal_char, replacement)
439+ result.append(partial)
440+ result = os.path.join(*result)
441+ if volume:
442+ result = volume + '\\' + result
443+ if prefix:
444+ result = LONG_PATH_PREFIX + result
445+ return unicode(result)
446+
447 def _set_file_attributes(path, groups):
448 """Set file attributes using the wind32api."""
449- security_descriptor = GetFileSecurity(path, DACL_SECURITY_INFORMATION)
450+ security_descriptor = None
451+ try:
452+ security_descriptor = GetFileSecurity(path, DACL_SECURITY_INFORMATION)
453+ except PyWinError, e:
454+ logger.exception('Got exception when setting file attributes %s ' +
455+ 'for path %s', path, groups)
456+ #check if the issue was due to an illegal path
457+ if getattr(e, "winerror", None) == 123:
458+ path = _remove_windows_illegal_chars(path)
459+ return _set_file_attributes(path, groups)
460+ else:
461+ raise
462+
463 dacl = ACL()
464 for group_name in groups:
465 # set the attributes of the group only if not null
466@@ -94,30 +189,15 @@
467 dacl.AddAccessAllowedAceEx(ACL_REVISION,
468 CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, groups[group_name],
469 group_sid)
470- # the dacl has all the info of the dff groups passed in the parameters
471+ # the dacl has all the info of the diff groups passed in the parameters
472 security_descriptor.SetSecurityDescriptorDacl(1, dacl, 0)
473 SetFileSecurity(path, DACL_SECURITY_INFORMATION, security_descriptor)
474
475
476-def _has_read_mask(number):
477- """Return if the read flag is present."""
478- # get the bin representation of the mask
479- binary = _int_to_bin(number)
480- # there is actual no documentation of this in MSDN but if bt 28 is set,
481- # the mask has full access, more info can be found here:
482- # http://www.iu.hio.no/cfengine/docs/cfengine-NT/node47.html
483- if binary[28] == '1':
484- return True
485- # there is no documentation in MSDN about this, but if bit 0 and 3 are true
486- # we have the read flag, more info can be found here:
487- # http://www.iu.hio.no/cfengine/docs/cfengine-NT/node47.html
488- return binary[0] == '1' and binary[3] == '1'
489-
490-
491 def absparam(paths_indexes=[], paths_names=[]):
492 """Ensure that the paths are absolute."""
493 def decorator(function):
494- """Decorate the funtion to make sure the paths are absolute."""
495+ """Decorate the function to make sure the paths are absolute."""
496 @wraps(function)
497 def abspath_wrapper(*args, **kwargs):
498 """Set the paths to be absolute."""
499@@ -131,7 +211,7 @@
500 kwargs[current_key] = \
501 os.path.abspath(kwargs[current_key])
502 except KeyError:
503- logging.warn('Patameter %s could not be made a long path',
504+ logger.warn('Parameter %s could not be made a long path',
505 current_key)
506 return function(*fixed_args, **kwargs)
507 return abspath_wrapper
508@@ -155,26 +235,36 @@
509 kwargs[current_key] = \
510 _set_as_long_path(kwargs[current_key])
511 except KeyError:
512- logging.warn('Patameter %s could not be made a long path',
513+ logger.warn('Parameter %s could not be made a long path',
514 current_key)
515 return function(*fixed_args, **kwargs)
516 return long_path_wrapper
517 return decorator
518
519
520+def _set_no_rights_helper(path):
521+ """Set the rights to be none."""
522+ os.chmod(path, 0o000)
523+ groups = {}
524+ _set_file_attributes(path, groups)
525+
526+
527 @longpath(paths_indexes=[0])
528 def set_no_rights(path):
529 # set the groups to be empty which will remove all the rights of the file
530- os.chmod(path, 0o000)
531- groups = {}
532- _set_file_attributes(path, groups)
533+ try:
534+ _set_no_rights_helper(path)
535+ except OSError, e:
536+ logger.exception('Got exception when setting path %s to no rights',
537+ path)
538+ return _check_winerror_123(e, _set_no_rights_helper, path)
539
540
541 @longpath(paths_indexes=[0])
542 def set_file_readonly(path):
543 """Change path permissions to readonly in a file."""
544 # we use the win32 api because chmod just sets the readonly flag and
545- # we want to have imore control over the permissions
546+ # we want to have more control over the permissions
547 groups = {}
548 groups[ADMINISTRATORS_GROUP] = FILE_GENERIC_READ | FILE_GENERIC_WRITE
549 groups[GetUserName()] = FILE_GENERIC_READ
550@@ -191,7 +281,12 @@
551 groups[GetUserName()] = FILE_ALL_ACCESS
552 # the above equals more or less to 0774
553 _set_file_attributes(path, groups)
554- os.chmod(path, stat.S_IWRITE)
555+ try:
556+ os.chmod(path, stat.S_IWRITE)
557+ except OSError, e:
558+ logger.exception('Get exception when trying to set readwrite ' +
559+ 'path %s', path)
560+ return _check_winerror_123(e, os.chmod, path, stat.S_IWRITE)
561
562
563 @longpath(paths_indexes=[0])
564@@ -229,7 +324,7 @@
565 @contextmanager
566 @longpath(paths_indexes=[0])
567 def allow_writes(path):
568- """A very simple context manager to allow writting in RO dirs."""
569+ """A very simple context manager to allow witting in RO dirs."""
570 # get the old dacl of the file so that we can reset it back when done
571 security_descriptor = GetFileSecurity(path, DACL_SECURITY_INFORMATION)
572 old_dacl = security_descriptor.GetSecurityDescriptorDacl()
573@@ -243,18 +338,28 @@
574 @longpath(paths_indexes=[0])
575 def remove_file(path):
576 """Remove a file."""
577- os.remove(path)
578+ try:
579+ os.remove(path)
580+ except OSError, e:
581+ logger.exception('Got exception when trying to remove path %s', path)
582+ return _check_winerror_123(e, os.remove, path)
583
584
585 @longpath(paths_indexes=[0])
586 def remove_dir(path):
587 """Remove a dir."""
588- os.rmdir(path)
589+ try:
590+ os.rmdir(path)
591+ except OSError, e:
592+ logger.exception('Got exception when trying to remove dir %s', path)
593+ return _check_winerror_123(e, os.rmdir, path)
594
595
596 @longpath(paths_indexes=[0])
597 def path_exists(path):
598 """Return if the path exists."""
599+ if _is_illegal_path(path):
600+ path = _remove_windows_illegal_chars(path)
601 return os.path.exists(path)
602
603
604@@ -264,37 +369,60 @@
605 if recursive:
606 try:
607 os.makedirs(path)
608- except WindowsError, e:
609- raise OSError(str(e))
610+ except OSError, e:
611+ logger.exception('Got exception when trying to make dirs %s', path)
612+ return _check_winerror_123(e, os.makedirs, path)
613 else:
614 try:
615 os.mkdir(path)
616- except WindowsError, e:
617- raise OSError(str(e))
618+ except OSError, e:
619+ logger.exception('Got exception when trying to make dirs %s', path)
620+ return _check_winerror_123(e, os.mkdir, path)
621
622
623 @longpath(paths_indexes=[0])
624 def open_file(path, mode='r'):
625 """Open a file."""
626- return open(path, mode)
627+ try:
628+ return open(path, mode)
629+ except OSError, e:
630+ logger.exception('Got exception when trying to open path %s', path)
631+ return _check_winerror_123(e, open, path, mode)
632
633
634 @absparam(paths_indexes=[0, 1])
635 @longpath(paths_indexes=[0, 1])
636 def rename(path_from, path_to):
637 """Rename a file or directory."""
638- # to ensure the same behavious as on linux, use the MoveEx function from
639- # win32 which will allow to replace the destionation path if exists and
640+ # to ensure the same behaviors as on linux, use the MoveEx function from
641+ # win32 which will allow to replace the destination path if exists and
642 # the user has the rights see:
643 # http://msdn.microsoft.com/en-us/library/aa365240(v=vs.85).aspx
644 try:
645- MoveFileEx(
646+ MoveFileExW(
647 path_from, path_to,
648 MOVEFILE_COPY_ALLOWED |
649 MOVEFILE_REPLACE_EXISTING |
650 MOVEFILE_WRITE_THROUGH)
651 except PyWinError, e:
652- raise OSError(str(e))
653+ logger.exception('Got exception when trying to rename from ' +
654+ '%s to %s', path_from, path_to)
655+ #check if the issue was due to an illegal path
656+ if getattr(e, "winerror", None) == 123:
657+ path_from = _remove_windows_illegal_chars(path_from)
658+ path_to = _remove_windows_illegal_chars(path_to)
659+ try:
660+ MoveFileExW(
661+ path_from, path_to,
662+ MOVEFILE_COPY_ALLOWED |
663+ MOVEFILE_REPLACE_EXISTING |
664+ MOVEFILE_WRITE_THROUGH)
665+ except PyWinError, e:
666+ logger.exception('Got exception when trying to rename' +
667+ ' from %s to %s', path_from, path_to)
668+ raise OSError(str(e))
669+ else:
670+ raise OSError(str(e))
671
672
673 @absparam(paths_indexes=[0, 1])
674@@ -343,7 +471,7 @@
675 @longpath(paths_indexes=[0])
676 def listdir(directory):
677 """List a directory."""
678- # os.listdir combines / and \ in its search which breacks things on
679+ # os.listdir combines / and \ in its search which breaks things on
680 # windows, we use the win32 api instead.
681 result = []
682 for file_info in FindFilesW(os.path.join(directory, '*.*')):
683@@ -357,8 +485,12 @@
684 @longpath(paths_indexes=[0])
685 def access(path):
686 """Return if the path is at least readable."""
687+ # lets consider the access on an illegal path to be a special case
688+ # since that will only occur in the case where the user created the path
689 # for a file to be readable it has to be readable either by the user or
690 # by the everyone group
691+ if _is_illegal_path(path):
692+ path = _remove_windows_illegal_chars(path)
693 if not os.path.exists(path):
694 return False
695 security_descriptor = GetFileSecurity(path, DACL_SECURITY_INFORMATION)
696@@ -380,13 +512,14 @@
697 # in our case we can ignore this since we are looking for visible
698 # users
699 continue
700- return (GetUserName() in accounts or EVERYONE_GROUP in accounts)
701+ return (GetUserName() in accounts or EVERYONE_GROUP in accounts) and\
702+ os.access(path, os.R_OK)
703
704
705 @longpath(paths_indexes=[0])
706 def can_write(path):
707 """Return if the path can be written to."""
708- file_att = GetFileAttributes(path)
709+ file_att = GetFileAttributes(path)
710 if file_att and FILE_ATTRIBUTE_READONLY:
711 return False
712 return True
713@@ -395,14 +528,18 @@
714 def stat_path(path, look_for_link=True):
715 """Return stat info about a path."""
716 # if the path end with .lnk, that means we are dealing with a link
717- # and we should return the stat of th target path
718+ # and we should return the stat of the target path
719 if path.endswith('.lnk'):
720 shell = Dispatch('WScript.Shell')
721 shortcut = shell.CreateShortCut(path)
722 path = shortcut.Targetpath
723 if look_for_link and os.path.exists(path + '.lnk'):
724 return stat_path(path + '.lnk')
725- return os.lstat(path)
726+ try:
727+ return os.lstat(path)
728+ except OSError, e:
729+ logger.exception('Got exception when trying to stat path %s', path)
730+ return _check_winerror_123(e, stat_path, path, look_for_link)
731
732
733 def move_to_trash(path):
734@@ -415,7 +552,7 @@
735 def set_application_name(app_name):
736 """Set the app name."""
737 # there is not way to do this on windows. The name will be correct when
738- # executed from a bundle .exe otherwhise it will be python
739+ # executed from a bundle .exe otherwise it will be python
740
741
742 def is_root():
743@@ -432,7 +569,7 @@
744
745
746 def validate_path_from_unix(path):
747- """Get a unix path and return it so that it cna be used."""
748+ """Get a unix path and return it so that it can be used."""
749 path = path.replace('/', '\\')
750 return path
751
752@@ -449,7 +586,9 @@
753
754 def abspath(path):
755 """Return an abspath."""
756- abs = os.path.abspath(path)
757+ # TODO: do we really need to do this, are the tests broken?
758+ path = path.replace('/', '\\')
759+ abs = os.path.abspath(_remove_windows_illegal_chars(path))
760 if not LONG_PATH_PREFIX in abs:
761 abs = LONG_PATH_PREFIX + abs
762 return abs
763\ No newline at end of file

Subscribers

People subscribed via source and target branches