1 # Copyright 2010-2013 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
4 from __future__ import unicode_literals
11 import shutil as _shutil
18 from portage import bsd_chflags, _encodings, _os_overrides, _selinux, \
19 _unicode_decode, _unicode_encode, _unicode_func_wrapper,\
20 _unicode_module_wrapper
21 from portage.const import MOVE_BINARY
22 from portage.exception import OperationNotSupported
23 from portage.localization import _
24 from portage.process import spawn
25 from portage.util import writemsg
27 def _apply_stat(src_stat, dest):
28 _os.chown(dest, src_stat.st_uid, src_stat.st_gid)
29 _os.chmod(dest, stat.S_IMODE(src_stat.st_mode))
31 _xattr_excluder_cache = {}
33 def _get_xattr_excluder(pattern):
36 value = _xattr_excluder_cache[pattern]
38 value = _xattr_excluder(pattern)
39 _xattr_excluder_cache[pattern] = value
43 class _xattr_excluder(object):
45 __slots__ = ('_pattern_split',)
47 def __init__(self, pattern):
50 self._pattern_split = None
52 pattern = pattern.split()
54 self._pattern_split = None
57 self._pattern_split = tuple(pattern)
59 def __call__(self, attr):
61 if self._pattern_split is None:
64 match = fnmatch.fnmatch
65 for x in self._pattern_split:
71 if hasattr(_os, "getxattr"):
72 # Python >=3.3 and GNU/Linux
73 def _copyxattr(src, dest, exclude=None):
75 attrs = _os.listxattr(src)
77 if exclude is not None and isinstance(attrs[0], bytes):
78 exclude = exclude.encode(_encodings['fs'])
79 exclude = _get_xattr_excluder(exclude)
85 _os.setxattr(dest, attr, _os.getxattr(src, attr))
86 raise_exception = False
88 raise_exception = True
90 raise OperationNotSupported(_("Filesystem containing file '%s' "
91 "does not support extended attribute '%s'") %
92 (_unicode_decode(dest), _unicode_decode(attr)))
99 def _copyxattr(src, dest, exclude=None):
102 attrs = xattr.list(src)
104 if e.errno != OperationNotSupported.errno:
109 if exclude is not None and isinstance(attrs[0], bytes):
110 exclude = exclude.encode(_encodings['fs'])
111 exclude = _get_xattr_excluder(exclude)
117 xattr.set(dest, attr, xattr.get(src, attr))
118 raise_exception = False
120 raise_exception = True
122 raise OperationNotSupported(_("Filesystem containing file '%s' "
123 "does not support extended attribute '%s'") %
124 (_unicode_decode(dest), _unicode_decode(attr)))
126 _devnull = open("/dev/null", "wb")
128 subprocess.call(["getfattr", "--version"], stdout=_devnull)
129 subprocess.call(["setfattr", "--version"], stdout=_devnull)
130 _has_getfattr_and_setfattr = True
132 _has_getfattr_and_setfattr = False
134 if _has_getfattr_and_setfattr:
135 def _copyxattr(src, dest, exclude=None):
136 # TODO: implement exclude
137 getfattr_process = subprocess.Popen(["getfattr", "-d", "--absolute-names", src], stdout=subprocess.PIPE)
138 getfattr_process.wait()
139 extended_attributes = getfattr_process.stdout.readlines()
140 getfattr_process.stdout.close()
141 if extended_attributes:
142 extended_attributes[0] = b"# file: " + _unicode_encode(dest) + b"\n"
143 setfattr_process = subprocess.Popen(["setfattr", "--restore=-"], stdin=subprocess.PIPE, stderr=subprocess.PIPE)
144 setfattr_process.communicate(input=b"".join(extended_attributes))
145 if setfattr_process.returncode != 0:
146 raise OperationNotSupported("Filesystem containing file '%s' does not support extended attributes" % dest)
148 def _copyxattr(src, dest, exclude=None):
151 def movefile(src, dest, newmtime=None, sstat=None, mysettings=None,
152 hardlink_candidates=None, encoding=_encodings['fs']):
153 """moves a file from src to dest, preserving all permissions and attributes; mtime will
154 be preserved even when moving across filesystems. Returns mtime as integer on success
155 and None on failure. mtime is expressed in seconds in Python <3.3 and nanoseconds in
156 Python >=3.3. Move is atomic."""
158 if mysettings is None:
159 mysettings = portage.settings
161 src_bytes = _unicode_encode(src, encoding=encoding, errors='strict')
162 dest_bytes = _unicode_encode(dest, encoding=encoding, errors='strict')
163 xattr_enabled = "xattr" in mysettings.features
164 selinux_enabled = mysettings.selinux_enabled()
166 selinux = _unicode_module_wrapper(_selinux, encoding=encoding)
167 _copyfile = selinux.copyfile
168 _rename = selinux.rename
170 _copyfile = _shutil.copyfile
173 lchown = _unicode_func_wrapper(portage.data.lchown, encoding=encoding)
174 os = _unicode_module_wrapper(_os,
175 encoding=encoding, overrides=_os_overrides)
181 except SystemExit as e:
183 except Exception as e:
184 writemsg("!!! %s\n" % _("Stating source file failed... movefile()"),
186 writemsg("!!! %s\n" % (e,), noiselevel=-1)
192 except (OSError, IOError):
193 dstat=os.lstat(os.path.dirname(dest))
197 if destexists and dstat.st_flags != 0:
198 bsd_chflags.lchflags(dest, 0)
199 # Use normal stat/chflags for the parent since we want to
200 # follow any symlinks to the real parent directory.
201 pflags = os.stat(os.path.dirname(dest)).st_flags
203 bsd_chflags.chflags(os.path.dirname(dest), 0)
206 if stat.S_ISLNK(dstat[stat.ST_MODE]):
210 except SystemExit as e:
212 except Exception as e:
215 if stat.S_ISLNK(sstat[stat.ST_MODE]):
217 target=os.readlink(src)
218 if mysettings and "D" in mysettings and \
219 target.startswith(mysettings["D"]):
220 target = target[len(mysettings["D"])-1:]
221 if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]):
225 selinux.symlink(target, dest, src)
227 os.symlink(target, dest)
229 # Some programs will create symlinks automatically, so we have
230 # to tolerate these links being recreated during the merge
231 # process. In any case, if the link is pointing at the right
232 # place, we're in good shape.
233 if e.errno not in (errno.ENOENT, errno.EEXIST) or \
234 target != os.readlink(dest):
236 lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
239 _os.unlink(src_bytes)
243 if sys.hexversion >= 0x3030000:
245 os.utime(dest, ns=(sstat.st_mtime_ns, sstat.st_mtime_ns), follow_symlinks=False)
246 except NotImplementedError:
247 # utimensat() and lutimes() missing in libc.
248 return os.stat(dest, follow_symlinks=False).st_mtime_ns
250 return sstat.st_mtime_ns
252 # utime() in Python <3.3 only works on the target of a symlink, so it's not
253 # possible to preserve mtime on symlinks.
254 return os.lstat(dest)[stat.ST_MTIME]
255 except SystemExit as e:
257 except Exception as e:
258 writemsg("!!! %s\n" % _("failed to properly create symlink:"),
260 writemsg("!!! %s -> %s\n" % (dest, target), noiselevel=-1)
261 writemsg("!!! %s\n" % (e,), noiselevel=-1)
265 # Since identical files might be merged to multiple filesystems,
266 # so os.link() calls might fail for some paths, so try them all.
267 # For atomic replacement, first create the link as a temp file
268 # and them use os.rename() to replace the destination.
269 if hardlink_candidates:
270 head, tail = os.path.split(dest)
271 hardlink_tmp = os.path.join(head, ".%s._portage_merge_.%s" % \
274 os.unlink(hardlink_tmp)
276 if e.errno != errno.ENOENT:
277 writemsg(_("!!! Failed to remove hardlink temp file: %s\n") % \
278 (hardlink_tmp,), noiselevel=-1)
279 writemsg("!!! %s\n" % (e,), noiselevel=-1)
282 for hardlink_src in hardlink_candidates:
284 os.link(hardlink_src, hardlink_tmp)
289 os.rename(hardlink_tmp, dest)
291 writemsg(_("!!! Failed to rename %s to %s\n") % \
292 (hardlink_tmp, dest), noiselevel=-1)
293 writemsg("!!! %s\n" % (e,), noiselevel=-1)
297 _os.unlink(src_bytes)
305 if not hardlinked and (selinux_enabled or sstat.st_dev == dstat.st_dev):
308 selinux.rename(src, dest)
313 if e.errno != errno.EXDEV:
315 writemsg("!!! %s\n" % _("Failed to move %(src)s to %(dest)s") %
316 {"src": src, "dest": dest}, noiselevel=-1)
317 writemsg("!!! %s\n" % (e,), noiselevel=-1)
319 # Invalid cross-device-link 'bind' mounted or actually Cross-Device
321 if stat.S_ISREG(sstat[stat.ST_MODE]):
322 dest_tmp = dest + "#new"
323 dest_tmp_bytes = _unicode_encode(dest_tmp, encoding=encoding,
325 try: # For safety copy then move it over.
326 _copyfile(src_bytes, dest_tmp_bytes)
329 _copyxattr(src_bytes, dest_tmp_bytes,
330 exclude=mysettings.get("PORTAGE_XATTR_EXCLUDE", "security.* system.nfs4_acl"))
334 msg = _("Failed to copy extended attributes. "
335 "In order to avoid this error, set "
336 "FEATURES=\"-xattr\" in make.conf.")
337 msg = textwrap.wrap(msg, 65)
339 writemsg("!!! %s\n" % (line,), noiselevel=-1)
341 _apply_stat(sstat, dest_tmp_bytes)
342 _rename(dest_tmp_bytes, dest_bytes)
343 _os.unlink(src_bytes)
344 except SystemExit as e:
346 except Exception as e:
347 writemsg("!!! %s\n" % _('copy %(src)s -> %(dest)s failed.') %
348 {"src": src, "dest": dest}, noiselevel=-1)
349 writemsg("!!! %s\n" % (e,), noiselevel=-1)
352 #we don't yet handle special, so we need to fall back to /bin/mv
353 a = spawn([MOVE_BINARY, '-f', src, dest], env=os.environ)
355 writemsg(_("!!! Failed to move special file:\n"), noiselevel=-1)
356 writemsg(_("!!! '%(src)s' to '%(dest)s'\n") % \
357 {"src": _unicode_decode(src, encoding=encoding),
358 "dest": _unicode_decode(dest, encoding=encoding)}, noiselevel=-1)
359 writemsg("!!! %s\n" % a, noiselevel=-1)
360 return None # failure
362 # In Python <3.3 always use stat_obj[stat.ST_MTIME] for the integral timestamp
363 # which is returned, since the stat_obj.st_mtime float attribute rounds *up*
364 # if the nanosecond part of the timestamp is 999999881 ns or greater.
367 if sys.hexversion >= 0x3030000:
368 newmtime = os.stat(dest).st_mtime_ns
370 newmtime = os.stat(dest)[stat.ST_MTIME]
372 # Note: It is not possible to preserve nanosecond precision
373 # (supported in POSIX.1-2008 via utimensat) with the IEEE 754
374 # double precision float which only has a 53 bit significand.
375 if newmtime is not None:
376 if sys.hexversion >= 0x3030000:
377 os.utime(dest, ns=(newmtime, newmtime))
379 os.utime(dest, (newmtime, newmtime))
381 if sys.hexversion >= 0x3030000:
382 newmtime = sstat.st_mtime_ns
384 newmtime = sstat[stat.ST_MTIME]
386 if sys.hexversion >= 0x3030000:
387 # If rename succeeded then timestamps are automatically
388 # preserved with complete precision because the source
389 # and destination inodes are the same. Otherwise, manually
390 # update timestamps with nanosecond precision.
391 os.utime(dest, ns=(newmtime, newmtime))
393 # If rename succeeded then timestamps are automatically
394 # preserved with complete precision because the source
395 # and destination inodes are the same. Otherwise, round
396 # down to the nearest whole second since python's float
397 # st_mtime cannot be used to preserve the st_mtim.tv_nsec
398 # field with complete precision. Note that we have to use
399 # stat_obj[stat.ST_MTIME] here because the float
400 # stat_obj.st_mtime rounds *up* sometimes.
401 os.utime(dest, (newmtime, newmtime))
403 # The utime can fail here with EPERM even though the move succeeded.
404 # Instead of failing, use stat to return the mtime if possible.
406 if sys.hexversion >= 0x3030000:
407 newmtime = os.stat(dest).st_mtime_ns
409 newmtime = os.stat(dest)[stat.ST_MTIME]
411 writemsg(_("!!! Failed to stat in movefile()\n"), noiselevel=-1)
412 writemsg("!!! %s\n" % dest, noiselevel=-1)
413 writemsg("!!! %s\n" % str(e), noiselevel=-1)
417 # Restore the flags we saved before moving
419 bsd_chflags.chflags(os.path.dirname(dest), pflags)