##// END OF EJS Templates
scmutil: fix erroneous Abort call...
Adrian Buehlmann -
r13973:366fa83f default
parent child Browse files
Show More
@@ -1,245 +1,245 b''
1 # scmutil.py - Mercurial core utility functions
1 # scmutil.py - Mercurial core utility functions
2 #
2 #
3 # Copyright Matt Mackall <mpm@selenic.com>
3 # Copyright Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 import util, error
9 import util, error
10 import os, errno, stat
10 import os, errno, stat
11
11
12 def checkportable(ui, f):
12 def checkportable(ui, f):
13 '''Check if filename f is portable and warn or abort depending on config'''
13 '''Check if filename f is portable and warn or abort depending on config'''
14 util.checkfilename(f)
14 util.checkfilename(f)
15 val = ui.config('ui', 'portablefilenames', 'warn')
15 val = ui.config('ui', 'portablefilenames', 'warn')
16 lval = val.lower()
16 lval = val.lower()
17 abort = os.name == 'nt' or lval == 'abort'
17 abort = os.name == 'nt' or lval == 'abort'
18 bval = util.parsebool(val)
18 bval = util.parsebool(val)
19 if abort or lval == 'warn' or bval:
19 if abort or lval == 'warn' or bval:
20 msg = util.checkwinfilename(f)
20 msg = util.checkwinfilename(f)
21 if msg:
21 if msg:
22 if abort:
22 if abort:
23 raise util.Abort("%s: %r" % (msg, f))
23 raise util.Abort("%s: %r" % (msg, f))
24 ui.warn(_("warning: %s: %r\n") % (msg, f))
24 ui.warn(_("warning: %s: %r\n") % (msg, f))
25 elif bval is None and lval != 'ignore':
25 elif bval is None and lval != 'ignore':
26 raise error.ConfigError(
26 raise error.ConfigError(
27 _("ui.portablefilenames value is invalid ('%s')") % val)
27 _("ui.portablefilenames value is invalid ('%s')") % val)
28
28
29 class path_auditor(object):
29 class path_auditor(object):
30 '''ensure that a filesystem path contains no banned components.
30 '''ensure that a filesystem path contains no banned components.
31 the following properties of a path are checked:
31 the following properties of a path are checked:
32
32
33 - ends with a directory separator
33 - ends with a directory separator
34 - under top-level .hg
34 - under top-level .hg
35 - starts at the root of a windows drive
35 - starts at the root of a windows drive
36 - contains ".."
36 - contains ".."
37 - traverses a symlink (e.g. a/symlink_here/b)
37 - traverses a symlink (e.g. a/symlink_here/b)
38 - inside a nested repository (a callback can be used to approve
38 - inside a nested repository (a callback can be used to approve
39 some nested repositories, e.g., subrepositories)
39 some nested repositories, e.g., subrepositories)
40 '''
40 '''
41
41
42 def __init__(self, root, callback=None):
42 def __init__(self, root, callback=None):
43 self.audited = set()
43 self.audited = set()
44 self.auditeddir = set()
44 self.auditeddir = set()
45 self.root = root
45 self.root = root
46 self.callback = callback
46 self.callback = callback
47
47
48 def __call__(self, path):
48 def __call__(self, path):
49 '''Check the relative path.
49 '''Check the relative path.
50 path may contain a pattern (e.g. foodir/**.txt)'''
50 path may contain a pattern (e.g. foodir/**.txt)'''
51
51
52 if path in self.audited:
52 if path in self.audited:
53 return
53 return
54 # AIX ignores "/" at end of path, others raise EISDIR.
54 # AIX ignores "/" at end of path, others raise EISDIR.
55 if util.endswithsep(path):
55 if util.endswithsep(path):
56 raise util.Abort(_("path ends in directory separator: %s") % path)
56 raise util.Abort(_("path ends in directory separator: %s") % path)
57 normpath = os.path.normcase(path)
57 normpath = os.path.normcase(path)
58 parts = util.splitpath(normpath)
58 parts = util.splitpath(normpath)
59 if (os.path.splitdrive(path)[0]
59 if (os.path.splitdrive(path)[0]
60 or parts[0].lower() in ('.hg', '.hg.', '')
60 or parts[0].lower() in ('.hg', '.hg.', '')
61 or os.pardir in parts):
61 or os.pardir in parts):
62 raise util.Abort(_("path contains illegal component: %s") % path)
62 raise util.Abort(_("path contains illegal component: %s") % path)
63 if '.hg' in path.lower():
63 if '.hg' in path.lower():
64 lparts = [p.lower() for p in parts]
64 lparts = [p.lower() for p in parts]
65 for p in '.hg', '.hg.':
65 for p in '.hg', '.hg.':
66 if p in lparts[1:]:
66 if p in lparts[1:]:
67 pos = lparts.index(p)
67 pos = lparts.index(p)
68 base = os.path.join(*parts[:pos])
68 base = os.path.join(*parts[:pos])
69 raise util.Abort(_('path %r is inside nested repo %r')
69 raise util.Abort(_('path %r is inside nested repo %r')
70 % (path, base))
70 % (path, base))
71
71
72 parts.pop()
72 parts.pop()
73 prefixes = []
73 prefixes = []
74 while parts:
74 while parts:
75 prefix = os.sep.join(parts)
75 prefix = os.sep.join(parts)
76 if prefix in self.auditeddir:
76 if prefix in self.auditeddir:
77 break
77 break
78 curpath = os.path.join(self.root, prefix)
78 curpath = os.path.join(self.root, prefix)
79 try:
79 try:
80 st = os.lstat(curpath)
80 st = os.lstat(curpath)
81 except OSError, err:
81 except OSError, err:
82 # EINVAL can be raised as invalid path syntax under win32.
82 # EINVAL can be raised as invalid path syntax under win32.
83 # They must be ignored for patterns can be checked too.
83 # They must be ignored for patterns can be checked too.
84 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
84 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
85 raise
85 raise
86 else:
86 else:
87 if stat.S_ISLNK(st.st_mode):
87 if stat.S_ISLNK(st.st_mode):
88 raise util.Abort(
88 raise util.Abort(
89 _('path %r traverses symbolic link %r')
89 _('path %r traverses symbolic link %r')
90 % (path, prefix))
90 % (path, prefix))
91 elif (stat.S_ISDIR(st.st_mode) and
91 elif (stat.S_ISDIR(st.st_mode) and
92 os.path.isdir(os.path.join(curpath, '.hg'))):
92 os.path.isdir(os.path.join(curpath, '.hg'))):
93 if not self.callback or not self.callback(curpath):
93 if not self.callback or not self.callback(curpath):
94 raise util.Abort(_('path %r is inside nested repo %r') %
94 raise util.Abort(_('path %r is inside nested repo %r') %
95 (path, prefix))
95 (path, prefix))
96 prefixes.append(prefix)
96 prefixes.append(prefix)
97 parts.pop()
97 parts.pop()
98
98
99 self.audited.add(path)
99 self.audited.add(path)
100 # only add prefixes to the cache after checking everything: we don't
100 # only add prefixes to the cache after checking everything: we don't
101 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
101 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
102 self.auditeddir.update(prefixes)
102 self.auditeddir.update(prefixes)
103
103
104 class opener(object):
104 class opener(object):
105 '''Open files relative to a base directory
105 '''Open files relative to a base directory
106
106
107 This class is used to hide the details of COW semantics and
107 This class is used to hide the details of COW semantics and
108 remote file access from higher level code.
108 remote file access from higher level code.
109 '''
109 '''
110 def __init__(self, base, audit=True):
110 def __init__(self, base, audit=True):
111 self.base = base
111 self.base = base
112 if audit:
112 if audit:
113 self.auditor = path_auditor(base)
113 self.auditor = path_auditor(base)
114 else:
114 else:
115 self.auditor = util.always
115 self.auditor = util.always
116 self.createmode = None
116 self.createmode = None
117 self._trustnlink = None
117 self._trustnlink = None
118
118
119 @util.propertycache
119 @util.propertycache
120 def _can_symlink(self):
120 def _can_symlink(self):
121 return util.checklink(self.base)
121 return util.checklink(self.base)
122
122
123 def _fixfilemode(self, name):
123 def _fixfilemode(self, name):
124 if self.createmode is None:
124 if self.createmode is None:
125 return
125 return
126 os.chmod(name, self.createmode & 0666)
126 os.chmod(name, self.createmode & 0666)
127
127
128 def __call__(self, path, mode="r", text=False, atomictemp=False):
128 def __call__(self, path, mode="r", text=False, atomictemp=False):
129 r = util.checkosfilename(path)
129 r = util.checkosfilename(path)
130 if r:
130 if r:
131 raise Abort("%s: %r" % (r, path))
131 raise util.Abort("%s: %r" % (r, path))
132 self.auditor(path)
132 self.auditor(path)
133 f = os.path.join(self.base, path)
133 f = os.path.join(self.base, path)
134
134
135 if not text and "b" not in mode:
135 if not text and "b" not in mode:
136 mode += "b" # for that other OS
136 mode += "b" # for that other OS
137
137
138 nlink = -1
138 nlink = -1
139 dirname, basename = os.path.split(f)
139 dirname, basename = os.path.split(f)
140 # If basename is empty, then the path is malformed because it points
140 # If basename is empty, then the path is malformed because it points
141 # to a directory. Let the posixfile() call below raise IOError.
141 # to a directory. Let the posixfile() call below raise IOError.
142 if basename and mode not in ('r', 'rb'):
142 if basename and mode not in ('r', 'rb'):
143 if atomictemp:
143 if atomictemp:
144 if not os.path.isdir(dirname):
144 if not os.path.isdir(dirname):
145 util.makedirs(dirname, self.createmode)
145 util.makedirs(dirname, self.createmode)
146 return util.atomictempfile(f, mode, self.createmode)
146 return util.atomictempfile(f, mode, self.createmode)
147 try:
147 try:
148 if 'w' in mode:
148 if 'w' in mode:
149 util.unlink(f)
149 util.unlink(f)
150 nlink = 0
150 nlink = 0
151 else:
151 else:
152 # nlinks() may behave differently for files on Windows
152 # nlinks() may behave differently for files on Windows
153 # shares if the file is open.
153 # shares if the file is open.
154 fd = util.posixfile(f)
154 fd = util.posixfile(f)
155 nlink = util.nlinks(f)
155 nlink = util.nlinks(f)
156 if nlink < 1:
156 if nlink < 1:
157 nlink = 2 # force mktempcopy (issue1922)
157 nlink = 2 # force mktempcopy (issue1922)
158 fd.close()
158 fd.close()
159 except (OSError, IOError), e:
159 except (OSError, IOError), e:
160 if e.errno != errno.ENOENT:
160 if e.errno != errno.ENOENT:
161 raise
161 raise
162 nlink = 0
162 nlink = 0
163 if not os.path.isdir(dirname):
163 if not os.path.isdir(dirname):
164 util.makedirs(dirname, self.createmode)
164 util.makedirs(dirname, self.createmode)
165 if nlink > 0:
165 if nlink > 0:
166 if self._trustnlink is None:
166 if self._trustnlink is None:
167 self._trustnlink = nlink > 1 or util.checknlink(f)
167 self._trustnlink = nlink > 1 or util.checknlink(f)
168 if nlink > 1 or not self._trustnlink:
168 if nlink > 1 or not self._trustnlink:
169 util.rename(util.mktempcopy(f), f)
169 util.rename(util.mktempcopy(f), f)
170 fp = util.posixfile(f, mode)
170 fp = util.posixfile(f, mode)
171 if nlink == 0:
171 if nlink == 0:
172 self._fixfilemode(f)
172 self._fixfilemode(f)
173 return fp
173 return fp
174
174
175 def symlink(self, src, dst):
175 def symlink(self, src, dst):
176 self.auditor(dst)
176 self.auditor(dst)
177 linkname = os.path.join(self.base, dst)
177 linkname = os.path.join(self.base, dst)
178 try:
178 try:
179 os.unlink(linkname)
179 os.unlink(linkname)
180 except OSError:
180 except OSError:
181 pass
181 pass
182
182
183 dirname = os.path.dirname(linkname)
183 dirname = os.path.dirname(linkname)
184 if not os.path.exists(dirname):
184 if not os.path.exists(dirname):
185 util.makedirs(dirname, self.createmode)
185 util.makedirs(dirname, self.createmode)
186
186
187 if self._can_symlink:
187 if self._can_symlink:
188 try:
188 try:
189 os.symlink(src, linkname)
189 os.symlink(src, linkname)
190 except OSError, err:
190 except OSError, err:
191 raise OSError(err.errno, _('could not symlink to %r: %s') %
191 raise OSError(err.errno, _('could not symlink to %r: %s') %
192 (src, err.strerror), linkname)
192 (src, err.strerror), linkname)
193 else:
193 else:
194 f = self(dst, "w")
194 f = self(dst, "w")
195 f.write(src)
195 f.write(src)
196 f.close()
196 f.close()
197 self._fixfilemode(dst)
197 self._fixfilemode(dst)
198
198
199 def canonpath(root, cwd, myname, auditor=None):
199 def canonpath(root, cwd, myname, auditor=None):
200 '''return the canonical path of myname, given cwd and root'''
200 '''return the canonical path of myname, given cwd and root'''
201 if util.endswithsep(root):
201 if util.endswithsep(root):
202 rootsep = root
202 rootsep = root
203 else:
203 else:
204 rootsep = root + os.sep
204 rootsep = root + os.sep
205 name = myname
205 name = myname
206 if not os.path.isabs(name):
206 if not os.path.isabs(name):
207 name = os.path.join(root, cwd, name)
207 name = os.path.join(root, cwd, name)
208 name = os.path.normpath(name)
208 name = os.path.normpath(name)
209 if auditor is None:
209 if auditor is None:
210 auditor = path_auditor(root)
210 auditor = path_auditor(root)
211 if name != rootsep and name.startswith(rootsep):
211 if name != rootsep and name.startswith(rootsep):
212 name = name[len(rootsep):]
212 name = name[len(rootsep):]
213 auditor(name)
213 auditor(name)
214 return util.pconvert(name)
214 return util.pconvert(name)
215 elif name == root:
215 elif name == root:
216 return ''
216 return ''
217 else:
217 else:
218 # Determine whether `name' is in the hierarchy at or beneath `root',
218 # Determine whether `name' is in the hierarchy at or beneath `root',
219 # by iterating name=dirname(name) until that causes no change (can't
219 # by iterating name=dirname(name) until that causes no change (can't
220 # check name == '/', because that doesn't work on windows). For each
220 # check name == '/', because that doesn't work on windows). For each
221 # `name', compare dev/inode numbers. If they match, the list `rel'
221 # `name', compare dev/inode numbers. If they match, the list `rel'
222 # holds the reversed list of components making up the relative file
222 # holds the reversed list of components making up the relative file
223 # name we want.
223 # name we want.
224 root_st = os.stat(root)
224 root_st = os.stat(root)
225 rel = []
225 rel = []
226 while True:
226 while True:
227 try:
227 try:
228 name_st = os.stat(name)
228 name_st = os.stat(name)
229 except OSError:
229 except OSError:
230 break
230 break
231 if util.samestat(name_st, root_st):
231 if util.samestat(name_st, root_st):
232 if not rel:
232 if not rel:
233 # name was actually the same as root (maybe a symlink)
233 # name was actually the same as root (maybe a symlink)
234 return ''
234 return ''
235 rel.reverse()
235 rel.reverse()
236 name = os.path.join(*rel)
236 name = os.path.join(*rel)
237 auditor(name)
237 auditor(name)
238 return util.pconvert(name)
238 return util.pconvert(name)
239 dirname, basename = os.path.split(name)
239 dirname, basename = os.path.split(name)
240 rel.append(basename)
240 rel.append(basename)
241 if dirname == name:
241 if dirname == name:
242 break
242 break
243 name = dirname
243 name = dirname
244
244
245 raise util.Abort('%s not under root' % myname)
245 raise util.Abort('%s not under root' % myname)
General Comments 0
You need to be logged in to leave comments. Login now