Ejemplo n.º 1
0
def parse_soname_deps(s):
    """
    Parse a REQUIRES or PROVIDES dependency string, and raise
    InvalidData if necessary.

    @param s: REQUIRES or PROVIDES string
    @type s: str
    @rtype: iter
    @return: An iterator of SonameAtom instances
    """

    categories = set()
    category = None
    previous_soname = None
    for soname in s.split():
        if soname.endswith(":"):
            if category is not None and previous_soname is None:
                raise InvalidData(_error_empty_category % category)

            category = soname[:-1]
            previous_soname = None
            if category in categories:
                raise InvalidData(_error_duplicate_category % category)
            categories.add(category)

        elif category is None:
            raise InvalidData(_error_missing_category % soname)
        else:
            previous_soname = soname
            yield SonameAtom(category, soname)

    if category is not None and previous_soname is None:
        raise InvalidData(_error_empty_category % category)
Ejemplo n.º 2
0
    def parse(cls, filename, line):
        """
		Parse a NEEDED.ELF.2 entry. Raises InvalidData if necessary.

		@param filename: file name for use in exception messages
		@type filename: str
		@param line: a single line of text from a NEEDED.ELF.2 file,
			without a trailing newline
		@type line: str
		@rtype: NeededEntry
		@return: A new NeededEntry instance containing data from line
		"""
        fields = line.split(";")
        if len(fields) < cls._MIN_FIELDS:
            raise InvalidData(
                _("Wrong number of fields "
                  "in %s: %s\n\n") % (filename, line))

        obj = cls()
        # Extra fields may exist (for future extensions).
        if (len(fields) > cls._MULTILIB_CAT_INDEX
                and fields[cls._MULTILIB_CAT_INDEX]):
            obj.multilib_category = fields[cls._MULTILIB_CAT_INDEX]
        else:
            obj.multilib_category = None

        del fields[cls._MIN_FIELDS:]
        obj.arch, obj.filename, obj.soname, rpaths, needed = fields
        obj.runpaths = tuple(filter(None, rpaths.split(":")))
        obj.needed = tuple(filter(None, needed.split(",")))

        return obj
Ejemplo n.º 3
0
    def __init__(self,
                 cpv,
                 metadata=None,
                 settings=None,
                 eapi=None,
                 repo=None,
                 slot=None):
        if not isinstance(cpv, _unicode):
            # Avoid TypeError from _unicode.__init__ with PyPy.
            cpv = _unicode_decode(cpv)
        _unicode.__init__(cpv)
        if metadata is not None:
            self.__dict__['_metadata'] = metadata
            slot = metadata.get('SLOT', slot)
            repo = metadata.get('repository', repo)
            eapi = metadata.get('EAPI', eapi)
        if settings is not None:
            self.__dict__['_settings'] = settings
        if eapi is not None:
            self.__dict__['eapi'] = eapi
        self.__dict__['cpv_split'] = catpkgsplit(cpv, eapi=eapi)
        if self.cpv_split is None:
            raise InvalidData(cpv)
        self.__dict__['cp'] = self.cpv_split[0] + '/' + self.cpv_split[1]
        if self.cpv_split[-1] == "r0" and cpv[-3:] != "-r0":
            self.__dict__['version'] = "-".join(self.cpv_split[2:-1])
        else:
            self.__dict__['version'] = "-".join(self.cpv_split[2:])
        # for match_from_list introspection
        self.__dict__['cpv'] = self
        if slot is not None:
            eapi_attrs = _get_eapi_attrs(eapi)
            slot_match = _get_slot_re(eapi_attrs).match(slot)
            if slot_match is None:
                # Avoid an InvalidAtom exception when creating SLOT atoms
                self.__dict__['slot'] = '0'
                self.__dict__['sub_slot'] = '0'
                self.__dict__['slot_invalid'] = slot
            else:
                if eapi_attrs.slot_operator:
                    slot_split = slot.split("/")
                    self.__dict__['slot'] = slot_split[0]
                    if len(slot_split) > 1:
                        self.__dict__['sub_slot'] = slot_split[1]
                    else:
                        self.__dict__['sub_slot'] = slot_split[0]
                else:
                    self.__dict__['slot'] = slot
                    self.__dict__['sub_slot'] = slot

        if repo is not None:
            repo = _gen_valid_repo(repo)
            if not repo:
                repo = _unknown_repo
            self.__dict__['repo'] = repo
Ejemplo n.º 4
0
def _parse_lafile_contents(contents):
	"""
	Parses 'dependency_libs' and 'inherited_linker_flags' lines.
	"""

	dep_libs = None
	inh_link_flags = None

	for line in contents.split(b"\n"):
		m = dep_libs_re.match(line)
		if m:
			if dep_libs is not None:
				raise InvalidData("duplicated dependency_libs entry")
			dep_libs = m.group("value")
			continue

		m = inh_link_flags_re.match(line)
		if m:
			if inh_link_flags is not None:
				raise InvalidData("duplicated inherited_linker_flags entry")
			inh_link_flags = m.group("value")
			continue

	return dep_libs, inh_link_flags
Ejemplo n.º 5
0
    def parse(cls, filename, line):
        """
        Parse a NEEDED.ELF.2 entry. Raises InvalidData if necessary.

        @param filename: file name for use in exception messages
        @type filename: str
        @param line: a single line of text from a NEEDED.ELF.2 file,
                without a trailing newline
        @type line: str
        @rtype: NeededEntry
        @return: A new NeededEntry instance containing data from line
        """
        fields = line.split(";")
        if len(fields) < cls._MIN_FIELDS:
            raise InvalidData(
                _("Wrong number of fields "
                  "in %s: %s\n\n") % (filename, line))

        obj = cls()
        # Extra fields may exist (for future extensions).
        if len(fields) > cls._MULTILIB_CAT_INDEX and fields[
                cls._MULTILIB_CAT_INDEX]:
            obj.multilib_category = fields[cls._MULTILIB_CAT_INDEX]
        else:
            obj.multilib_category = None

        del fields[cls._MIN_FIELDS:]
        obj.arch, obj.filename, obj.soname, rpaths, needed = fields
        # We don't use scanelf -q, since that would omit libraries like
        # musl's /usr/lib/libc.so which do not have any DT_NEEDED or
        # DT_SONAME settings. Since we don't use scanelf -q, we have to
        # handle the special rpath value "  -  " below.
        rpaths = "" if rpaths == "  -  " else rpaths
        obj.runpaths = tuple(filter(None, rpaths.split(":")))
        obj.needed = tuple(filter(None, needed.split(",")))

        return obj
Ejemplo n.º 6
0
def _lockfile_iteration(mypath,
                        wantnewlockfile=False,
                        unlinkfile=False,
                        waiting_msg=None,
                        flags=0):
    """
	Acquire a lock on mypath, without retry. Return None if the lockfile
	was removed by previous lock holder (caller must retry).

	@param mypath: lock file path
	@type mypath: str
	@param wantnewlockfile: use a separate new lock file
	@type wantnewlockfile: bool
	@param unlinkfile: remove lock file prior to unlock
	@type unlinkfile: bool
	@param waiting_msg: message to show before blocking
	@type waiting_msg: str
	@param flags: lock flags (only supports os.O_NONBLOCK)
	@type flags: int
	@rtype: bool
	@return: unlockfile tuple on success, None if retry is needed
	"""
    if not mypath:
        raise InvalidData(_("Empty path given"))

    # Since Python 3.4, chown requires int type (no proxies).
    portage_gid = int(portage.data.portage_gid)

    # Support for file object or integer file descriptor parameters is
    # deprecated due to ambiguity in whether or not it's safe to close
    # the file descriptor, making it prone to "Bad file descriptor" errors
    # or file descriptor leaks.
    if isinstance(mypath, basestring) and mypath[-1] == '/':
        mypath = mypath[:-1]

    lockfilename_path = mypath
    if hasattr(mypath, 'fileno'):
        warnings.warn(
            "portage.locks.lockfile() support for "
            "file object parameters is deprecated. Use a file path instead.",
            DeprecationWarning,
            stacklevel=2)
        lockfilename_path = getattr(mypath, 'name', None)
        mypath = mypath.fileno()
    if isinstance(mypath, int):
        warnings.warn(
            "portage.locks.lockfile() support for integer file "
            "descriptor parameters is deprecated. Use a file path instead.",
            DeprecationWarning,
            stacklevel=2)
        lockfilename = mypath
        wantnewlockfile = 0
        unlinkfile = 0
    elif wantnewlockfile:
        base, tail = os.path.split(mypath)
        lockfilename = os.path.join(base, "." + tail + ".portage_lockfile")
        lockfilename_path = lockfilename
        unlinkfile = 1
    else:
        lockfilename = mypath

    if isinstance(mypath, basestring):
        if not os.path.exists(os.path.dirname(mypath)):
            raise DirectoryNotFound(os.path.dirname(mypath))
        preexisting = os.path.exists(lockfilename)
        old_mask = os.umask(000)
        try:
            while True:
                try:
                    myfd = os.open(lockfilename, os.O_CREAT | os.O_RDWR, 0o660)
                except OSError as e:
                    if e.errno in (errno.ENOENT,
                                   errno.ESTALE) and os.path.isdir(
                                       os.path.dirname(lockfilename)):
                        # Retry required for NFS (see bug 636798).
                        continue
                    else:
                        _raise_exc(e)
                else:
                    break

            if not preexisting:
                try:
                    if portage.data.secpass >= 1 and os.stat(
                            lockfilename).st_gid != portage_gid:
                        os.chown(lockfilename, -1, portage_gid)
                except OSError as e:
                    if e.errno in (errno.ENOENT, errno.ESTALE):
                        os.close(myfd)
                        return None
                    else:
                        writemsg("%s: chown('%s', -1, %d)\n" % \
                         (e, lockfilename, portage_gid), noiselevel=-1)
                        writemsg(_("Cannot chown a lockfile: '%s'\n") % \
                         lockfilename, noiselevel=-1)
                        writemsg(_("Group IDs of current user: %s\n") % \
                         " ".join(str(n) for n in os.getgroups()),
                         noiselevel=-1)
        finally:
            os.umask(old_mask)

    elif isinstance(mypath, int):
        myfd = mypath

    else:
        raise ValueError(_("Unknown type passed in '%s': '%s'") % \
         (type(mypath), mypath))

    # try for a non-blocking lock, if it's held, throw a message
    # we're waiting on lockfile and use a blocking attempt.
    locking_method = portage._eintr_func_wrapper(_get_lock_fn())
    try:
        if "__PORTAGE_TEST_HARDLINK_LOCKS" in os.environ:
            raise IOError(errno.ENOSYS, "Function not implemented")
        locking_method(myfd, fcntl.LOCK_EX | fcntl.LOCK_NB)
    except IOError as e:
        if not hasattr(e, "errno"):
            raise
        if e.errno in (errno.EACCES, errno.EAGAIN, errno.ENOLCK):
            # resource temp unavailable; eg, someone beat us to the lock.
            if flags & os.O_NONBLOCK:
                os.close(myfd)
                raise TryAgain(mypath)

            global _quiet
            if _quiet:
                out = None
            else:
                out = portage.output.EOutput()
            if waiting_msg is None:
                if isinstance(mypath, int):
                    waiting_msg = _("waiting for lock on fd %i") % myfd
                else:
                    waiting_msg = _("waiting for lock on %s") % lockfilename
            if out is not None:
                out.ebegin(waiting_msg)
            # try for the exclusive lock now.
            enolock_msg_shown = False
            while True:
                try:
                    locking_method(myfd, fcntl.LOCK_EX)
                except EnvironmentError as e:
                    if e.errno == errno.ENOLCK:
                        # This is known to occur on Solaris NFS (see
                        # bug #462694). Assume that the error is due
                        # to temporary exhaustion of record locks,
                        # and loop until one becomes available.
                        if not enolock_msg_shown:
                            enolock_msg_shown = True
                            if isinstance(mypath, int):
                                context_desc = _("Error while waiting "
                                                 "to lock fd %i") % myfd
                            else:
                                context_desc = _("Error while waiting "
                                                 "to lock '%s'") % lockfilename
                            writemsg("\n!!! %s: %s\n" % (context_desc, e),
                                     noiselevel=-1)

                        time.sleep(_HARDLINK_POLL_LATENCY)
                        continue

                    if out is not None:
                        out.eend(1, str(e))
                    raise
                else:
                    break

            if out is not None:
                out.eend(os.EX_OK)
        elif e.errno in (errno.ENOSYS, ):
            # We're not allowed to lock on this FS.
            if not isinstance(lockfilename, int):
                # If a file object was passed in, it's not safe
                # to close the file descriptor because it may
                # still be in use.
                os.close(myfd)
            lockfilename_path = _unicode_decode(lockfilename_path,
                                                encoding=_encodings['fs'],
                                                errors='strict')
            if not isinstance(lockfilename_path, basestring):
                raise
            link_success = hardlink_lockfile(lockfilename_path,
                                             waiting_msg=waiting_msg,
                                             flags=flags)
            if not link_success:
                raise
            lockfilename = lockfilename_path
            locking_method = None
            myfd = HARDLINK_FD
        else:
            raise

    if isinstance(lockfilename,
                  basestring) and myfd != HARDLINK_FD and unlinkfile:
        try:
            removed = _lockfile_was_removed(myfd, lockfilename)
        except Exception:
            # Do not leak the file descriptor here.
            os.close(myfd)
            raise
        else:
            if removed:
                # Removed by previous lock holder... Caller will retry...
                os.close(myfd)
                return None

    if myfd != HARDLINK_FD:

        # FD_CLOEXEC is enabled by default in Python >=3.4.
        if sys.hexversion < 0x3040000:
            try:
                fcntl.FD_CLOEXEC
            except AttributeError:
                pass
            else:
                fcntl.fcntl(
                    myfd, fcntl.F_SETFD,
                    fcntl.fcntl(myfd, fcntl.F_GETFD) | fcntl.FD_CLOEXEC)

        _open_fds.add(myfd)

    writemsg(str((lockfilename, myfd, unlinkfile)) + "\n", 1)
    return (lockfilename, myfd, unlinkfile, locking_method)
Ejemplo n.º 7
0
    def __init__(
        self,
        cpv,
        metadata=None,
        settings=None,
        eapi=None,
        repo=None,
        slot=None,
        build_time=None,
        build_id=None,
        file_size=None,
        mtime=None,
        db=None,
    ):
        if not isinstance(cpv, str):
            # Avoid TypeError from str.__init__ with PyPy.
            cpv = _unicode_decode(cpv)
        str.__init__(cpv)
        if metadata is not None:
            self.__dict__["_metadata"] = metadata
            slot = metadata.get("SLOT", slot)
            repo = metadata.get("repository", repo)
            eapi = metadata.get("EAPI", eapi)
            build_time = metadata.get("BUILD_TIME", build_time)
            file_size = metadata.get("SIZE", file_size)
            build_id = metadata.get("BUILD_ID", build_id)
            mtime = metadata.get("_mtime_", mtime)
        if settings is not None:
            self.__dict__["_settings"] = settings
        if db is not None:
            self.__dict__["_db"] = db
        if eapi is not None:
            self.__dict__["eapi"] = eapi

        self.__dict__["build_time"] = self._long(build_time, 0)
        self.__dict__["file_size"] = self._long(file_size, None)
        self.__dict__["build_id"] = self._long(build_id, None)
        self.__dict__["mtime"] = self._long(mtime, None)
        self.__dict__["cpv_split"] = catpkgsplit(cpv, eapi=eapi)
        if self.cpv_split is None:
            raise InvalidData(cpv)
        self.__dict__["cp"] = self.cpv_split[0] + "/" + self.cpv_split[1]
        if self.cpv_split[-1] == "r0" and cpv[-3:] != "-r0":
            self.__dict__["version"] = "-".join(self.cpv_split[2:-1])
        else:
            self.__dict__["version"] = "-".join(self.cpv_split[2:])
        # for match_from_list introspection
        self.__dict__["cpv"] = self
        if slot is not None:
            eapi_attrs = _get_eapi_attrs(eapi)
            slot_match = _get_slot_re(eapi_attrs).match(slot)
            if slot_match is None:
                # Avoid an InvalidAtom exception when creating SLOT atoms
                self.__dict__["slot"] = "0"
                self.__dict__["sub_slot"] = "0"
                self.__dict__["slot_invalid"] = slot
            else:
                if eapi_attrs.slot_operator:
                    slot_split = slot.split("/")
                    self.__dict__["slot"] = slot_split[0]
                    if len(slot_split) > 1:
                        self.__dict__["sub_slot"] = slot_split[1]
                    else:
                        self.__dict__["sub_slot"] = slot_split[0]
                else:
                    self.__dict__["slot"] = slot
                    self.__dict__["sub_slot"] = slot

        if repo is not None:
            repo = _gen_valid_repo(repo)
            if not repo:
                repo = _unknown_repo
            self.__dict__["repo"] = repo
Ejemplo n.º 8
0
def rewrite_lafile(contents):
	"""
	Given the contents of an .la file, parse and fix it.
	This operates with strings of raw bytes (assumed to contain some ascii
	characters), in order to avoid any potential character encoding issues.
	Raises 'InvalidData' if the .la file is invalid.
	@param contents: the contents of a libtool archive file
	@type contents: bytes
	@rtype: tuple
	@return: (True, fixed_contents) if something needed to be
		fixed, (False, None) otherwise.
	"""
	#Parse the 'dependency_libs' and 'inherited_linker_flags' lines.
	dep_libs, inh_link_flags = \
		_parse_lafile_contents(contents)

	if dep_libs is None:
		raise InvalidData("missing or invalid dependency_libs")

	new_dep_libs = []
	new_inh_link_flags = []
	librpath = []
	libladir = []

	if inh_link_flags is not None:
		new_inh_link_flags = inh_link_flags.split()

	#Check entries in 'dependency_libs'.
	for dep_libs_entry in dep_libs.split():
		if dep_libs_entry.startswith(b"-l"):
			#-lfoo, keep it
			if dep_libs_entry not in new_dep_libs:
				new_dep_libs.append(dep_libs_entry)

		elif dep_libs_entry.endswith(b".la"):
			#Two cases:
			#1) /usr/lib64/libfoo.la, turn it into -lfoo and append -L/usr/lib64 to libladir
			#2) libfoo.la, keep it
			dir, file = _os.path.split(dep_libs_entry)

			if not dir or not file.startswith(b"lib"):
				if dep_libs_entry not in new_dep_libs:
					new_dep_libs.append(dep_libs_entry)
			else:
				#/usr/lib64/libfoo.la -> -lfoo
				lib = b"-l" + file[3:-3]
				if lib not in new_dep_libs:
					new_dep_libs.append(lib)
				#/usr/lib64/libfoo.la -> -L/usr/lib64
				ladir = b"-L" + dir
				if ladir not in libladir:
					libladir.append(ladir)

		elif dep_libs_entry.startswith(b"-L"):
			#Do some replacement magic and store them in 'libladir'.
			#This allows us to place all -L entries at the beginning
			#of 'dependency_libs'.
			ladir = dep_libs_entry

			ladir = X11_local_sub.sub(b"lib", ladir)
			ladir = pkgconfig_sub1.sub(b"usr", ladir)
			ladir = pkgconfig_sub2.sub(br"\g<usrlib>", ladir)

			if ladir not in libladir:
				libladir.append(ladir)

		elif dep_libs_entry.startswith(b"-R"):
			if dep_libs_entry not in librpath:
				librpath.append(dep_libs_entry)

		elif flag_re.match(dep_libs_entry):
			#All this stuff goes into inh_link_flags, if the la file has such an entry.
			#If it doesn't, they stay in 'dependency_libs'.
			if inh_link_flags is not None:
				if dep_libs_entry not in new_inh_link_flags:
					new_inh_link_flags.append(dep_libs_entry)
			else:
				if dep_libs_entry not in new_dep_libs:
					new_dep_libs.append(dep_libs_entry)

		else:
			raise InvalidData("Error: Unexpected entry '%s' in 'dependency_libs'" \
				% _unicode_decode(dep_libs_entry))

	#What should 'dependency_libs' and 'inherited_linker_flags' look like?
	expected_dep_libs = b""
	for x in (librpath, libladir, new_dep_libs):
		if x:
			expected_dep_libs += b" " + b" ".join(x)

	expected_inh_link_flags = b""
	if new_inh_link_flags:
		expected_inh_link_flags += b" " + b" ".join(new_inh_link_flags)

	#Don't touch the file if we don't need to, otherwise put the expected values into
	#'contents' and write it into the la file.

	changed = False
	if dep_libs != expected_dep_libs:
		contents = contents.replace(b"dependency_libs='" + dep_libs + b"'", \
			b"dependency_libs='" + expected_dep_libs + b"'")
		changed = True

	if inh_link_flags is not None and expected_inh_link_flags != inh_link_flags:
		contents = contents.replace(b"inherited_linker_flags='" + inh_link_flags + b"'", \
			b"inherited_linker_flags='" + expected_inh_link_flags + b"'")
		changed = True

	if changed:
		return True, contents
	else:
		return False, None
Ejemplo n.º 9
0
def lockfile(mypath,
             wantnewlockfile=0,
             unlinkfile=0,
             waiting_msg=None,
             flags=0):
    """
	If wantnewlockfile is True then this creates a lockfile in the parent
	directory as the file: '.' + basename + '.portage_lockfile'.
	"""

    if not mypath:
        raise InvalidData(_("Empty path given"))

    # Support for file object or integer file descriptor parameters is
    # deprecated due to ambiguity in whether or not it's safe to close
    # the file descriptor, making it prone to "Bad file descriptor" errors
    # or file descriptor leaks.
    if isinstance(mypath, basestring) and mypath[-1] == '/':
        mypath = mypath[:-1]

    lockfilename_path = mypath
    if hasattr(mypath, 'fileno'):
        warnings.warn(
            "portage.locks.lockfile() support for "
            "file object parameters is deprecated. Use a file path instead.",
            DeprecationWarning,
            stacklevel=2)
        lockfilename_path = getattr(mypath, 'name', None)
        mypath = mypath.fileno()
    if isinstance(mypath, int):
        warnings.warn(
            "portage.locks.lockfile() support for integer file "
            "descriptor parameters is deprecated. Use a file path instead.",
            DeprecationWarning,
            stacklevel=2)
        lockfilename = mypath
        wantnewlockfile = 0
        unlinkfile = 0
    elif wantnewlockfile:
        base, tail = os.path.split(mypath)
        lockfilename = os.path.join(base, "." + tail + ".portage_lockfile")
        lockfilename_path = lockfilename
        unlinkfile = 1
    else:
        lockfilename = mypath

    if isinstance(mypath, basestring):
        if not os.path.exists(os.path.dirname(mypath)):
            raise DirectoryNotFound(os.path.dirname(mypath))
        preexisting = os.path.exists(lockfilename)
        old_mask = os.umask(000)
        try:
            try:
                myfd = os.open(lockfilename, os.O_CREAT | os.O_RDWR, 0o660)
            except OSError as e:
                func_call = "open('%s')" % lockfilename
                if e.errno == OperationNotPermitted.errno:
                    raise OperationNotPermitted(func_call)
                elif e.errno == PermissionDenied.errno:
                    raise PermissionDenied(func_call)
                else:
                    raise

            if not preexisting:
                try:
                    if os.stat(lockfilename).st_gid != portage_gid:
                        os.chown(lockfilename, -1, portage_gid)
                except OSError as e:
                    if e.errno in (errno.ENOENT, errno.ESTALE):
                        return lockfile(mypath,
                                        wantnewlockfile=wantnewlockfile,
                                        unlinkfile=unlinkfile,
                                        waiting_msg=waiting_msg,
                                        flags=flags)
                    else:
                        writemsg("%s: chown('%s', -1, %d)\n" % \
                         (e, lockfilename, portage_gid), noiselevel=-1)
                        writemsg(_("Cannot chown a lockfile: '%s'\n") % \
                         lockfilename, noiselevel=-1)
                        writemsg(_("Group IDs of current user: %s\n") % \
                         " ".join(str(n) for n in os.getgroups()),
                         noiselevel=-1)
        finally:
            os.umask(old_mask)

    elif isinstance(mypath, int):
        myfd = mypath

    else:
        raise ValueError(_("Unknown type passed in '%s': '%s'") % \
         (type(mypath), mypath))

    # try for a non-blocking lock, if it's held, throw a message
    # we're waiting on lockfile and use a blocking attempt.
    locking_method = _default_lock_fn
    try:
        if "__PORTAGE_TEST_HARDLINK_LOCKS" in os.environ:
            raise IOError(errno.ENOSYS, "Function not implemented")
        locking_method(myfd, fcntl.LOCK_EX | fcntl.LOCK_NB)
    except IOError as e:
        if not hasattr(e, "errno"):
            raise
        if e.errno in (errno.EACCES, errno.EAGAIN):
            # resource temp unavailable; eg, someone beat us to the lock.
            if flags & os.O_NONBLOCK:
                os.close(myfd)
                raise TryAgain(mypath)

            global _quiet
            if _quiet:
                out = None
            else:
                out = portage.output.EOutput()
            if waiting_msg is None:
                if isinstance(mypath, int):
                    waiting_msg = _("waiting for lock on fd %i") % myfd
                else:
                    waiting_msg = _("waiting for lock on %s\n") % lockfilename
            if out is not None:
                out.ebegin(waiting_msg)
            # try for the exclusive lock now.
            try:
                locking_method(myfd, fcntl.LOCK_EX)
            except EnvironmentError as e:
                if out is not None:
                    out.eend(1, str(e))
                raise
            if out is not None:
                out.eend(os.EX_OK)
        elif e.errno in (errno.ENOSYS, errno.ENOLCK):
            # We're not allowed to lock on this FS.
            if not isinstance(lockfilename, int):
                # If a file object was passed in, it's not safe
                # to close the file descriptor because it may
                # still be in use.
                os.close(myfd)
            lockfilename_path = _unicode_decode(lockfilename_path,
                                                encoding=_encodings['fs'],
                                                errors='strict')
            if not isinstance(lockfilename_path, basestring):
                raise
            link_success = hardlink_lockfile(lockfilename_path,
                                             waiting_msg=waiting_msg,
                                             flags=flags)
            if not link_success:
                raise
            lockfilename = lockfilename_path
            locking_method = None
            myfd = HARDLINK_FD
        else:
            raise


    if isinstance(lockfilename, basestring) and \
     myfd != HARDLINK_FD and _fstat_nlink(myfd) == 0:
        # The file was deleted on us... Keep trying to make one...
        os.close(myfd)
        writemsg(_("lockfile recurse\n"), 1)
        lockfilename, myfd, unlinkfile, locking_method = lockfile(
            mypath,
            wantnewlockfile=wantnewlockfile,
            unlinkfile=unlinkfile,
            waiting_msg=waiting_msg,
            flags=flags)

    if myfd != HARDLINK_FD:
        _open_fds.add(myfd)

    writemsg(str((lockfilename, myfd, unlinkfile)) + "\n", 1)
    return (lockfilename, myfd, unlinkfile, locking_method)
Ejemplo n.º 10
0
def lockfile(mypath,
             wantnewlockfile=0,
             unlinkfile=0,
             waiting_msg=None,
             flags=0):
    """
	If wantnewlockfile is True then this creates a lockfile in the parent
	directory as the file: '.' + basename + '.portage_lockfile'.
	"""
    import fcntl

    if not mypath:
        raise InvalidData(_("Empty path given"))

    if isinstance(mypath, basestring) and mypath[-1] == '/':
        mypath = mypath[:-1]

    if hasattr(mypath, 'fileno'):
        mypath = mypath.fileno()
    if isinstance(mypath, int):
        lockfilename = mypath
        wantnewlockfile = 0
        unlinkfile = 0
    elif wantnewlockfile:
        base, tail = os.path.split(mypath)
        lockfilename = os.path.join(base, "." + tail + ".portage_lockfile")
        del base, tail
        unlinkfile = 1
    else:
        lockfilename = mypath

    if isinstance(mypath, basestring):
        if not os.path.exists(os.path.dirname(mypath)):
            raise DirectoryNotFound(os.path.dirname(mypath))
        preexisting = os.path.exists(lockfilename)
        old_mask = os.umask(000)
        try:
            try:
                myfd = os.open(lockfilename, os.O_CREAT | os.O_RDWR, 0o660)
            except OSError as e:
                func_call = "open('%s')" % lockfilename
                if e.errno == OperationNotPermitted.errno:
                    raise OperationNotPermitted(func_call)
                elif e.errno == PermissionDenied.errno:
                    raise PermissionDenied(func_call)
                else:
                    raise

            if not preexisting:
                try:
                    if os.stat(lockfilename).st_gid != portage_gid:
                        os.chown(lockfilename, -1, portage_gid)
                except OSError as e:
                    if e.errno in (errno.ENOENT, errno.ESTALE):
                        return lockfile(mypath,
                                        wantnewlockfile=wantnewlockfile,
                                        unlinkfile=unlinkfile,
                                        waiting_msg=waiting_msg,
                                        flags=flags)
                    else:
                        writemsg("%s: chown('%s', -1, %d)\n" % \
                         (e, lockfilename, portage_gid), noiselevel=-1)
                        writemsg(_("Cannot chown a lockfile: '%s'\n") % \
                         lockfilename, noiselevel=-1)
                        writemsg(_("Group IDs of current user: %s\n") % \
                         " ".join(str(n) for n in os.getgroups()),
                         noiselevel=-1)
        finally:
            os.umask(old_mask)

    elif isinstance(mypath, int):
        myfd = mypath

    else:
        raise ValueError(_("Unknown type passed in '%s': '%s'") % \
         (type(mypath), mypath))

    # try for a non-blocking lock, if it's held, throw a message
    # we're waiting on lockfile and use a blocking attempt.
    locking_method = fcntl.lockf
    try:
        fcntl.lockf(myfd, fcntl.LOCK_EX | fcntl.LOCK_NB)
    except IOError as e:
        if "errno" not in dir(e):
            raise
        if e.errno in (errno.EACCES, errno.EAGAIN):
            # resource temp unavailable; eg, someone beat us to the lock.
            if flags & os.O_NONBLOCK:
                raise TryAgain(mypath)

            global _quiet
            out = EOutput()
            out.quiet = _quiet
            if waiting_msg is None:
                if isinstance(mypath, int):
                    waiting_msg = _("waiting for lock on fd %i") % myfd
                else:
                    waiting_msg = _("waiting for lock on %s\n") % lockfilename
            out.ebegin(waiting_msg)
            # try for the exclusive lock now.
            try:
                fcntl.lockf(myfd, fcntl.LOCK_EX)
            except EnvironmentError as e:
                out.eend(1, str(e))
                raise
            out.eend(os.EX_OK)
        elif e.errno == errno.ENOLCK:
            # We're not allowed to lock on this FS.
            os.close(myfd)
            link_success = False
            if lockfilename == str(lockfilename):
                if wantnewlockfile:
                    try:
                        if os.stat(lockfilename)[stat.ST_NLINK] == 1:
                            os.unlink(lockfilename)
                    except OSError:
                        pass
                    link_success = hardlink_lockfile(lockfilename)
            if not link_success:
                raise
            locking_method = None
            myfd = HARDLINK_FD
        else:
            raise


    if isinstance(lockfilename, basestring) and \
     myfd != HARDLINK_FD and _fstat_nlink(myfd) == 0:
        # The file was deleted on us... Keep trying to make one...
        os.close(myfd)
        writemsg(_("lockfile recurse\n"), 1)
        lockfilename, myfd, unlinkfile, locking_method = lockfile(
            mypath,
            wantnewlockfile=wantnewlockfile,
            unlinkfile=unlinkfile,
            waiting_msg=waiting_msg,
            flags=flags)

    writemsg(str((lockfilename, myfd, unlinkfile)) + "\n", 1)
    return (lockfilename, myfd, unlinkfile, locking_method)