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