summaryrefslogtreecommitdiff
blob: 2dd97237b8f03ca94e71523c64b4dd1b6a367053 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# Copyright 2013-2019 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2

import errno
import logging

from portage import os
from portage.package.ebuild.fetch import ContentHashLayout
from portage.util._async.FileCopier import FileCopier
from _emerge.CompositeTask import CompositeTask

logger = logging.getLogger(__name__)


class DeletionTask(CompositeTask):
    __slots__ = ("distfile", "distfile_path", "config")

    def _start(self):
        if self.config.options.recycle_dir is not None:
            recycle_path = os.path.join(self.config.options.recycle_dir, self.distfile)
            if self.config.options.dry_run:
                logger.info(
                    f"dry-run: move '{self.distfile}' from distfiles to recycle"
                )
            else:
                logger.debug(f"move '{self.distfile}' from distfiles to recycle")
                try:
                    # note: distfile_path can be a symlink here
                    os.rename(os.path.realpath(self.distfile_path), recycle_path)
                except OSError as e:
                    if e.errno != errno.EXDEV:
                        logger.error(
                            ("rename %s from distfiles to " "recycle failed: %s")
                            % (self.distfile, e)
                        )
                else:
                    self._delete_links()
                    self._async_wait()
                    return

                self._start_task(
                    FileCopier(
                        src_path=self.distfile_path,
                        dest_path=recycle_path,
                        background=False,
                    ),
                    self._recycle_copier_exit,
                )
                return

        success = True

        if self.config.options.dry_run:
            logger.info(f"dry-run: delete '{self.distfile}' from distfiles")
        else:
            logger.debug(f"delete '{self.distfile}' from distfiles")
            try:
                os.unlink(self.distfile_path)
            except OSError as e:
                if e.errno not in (errno.ENOENT, errno.ESTALE):
                    logger.error(f"{self.distfile} unlink failed in distfiles: {e}")
                    success = False

        if success:
            self._delete_links()
        else:
            self.returncode = 1

        self._async_wait()

    def _recycle_copier_exit(self, copier):
        self._assert_current(copier)
        if self._was_cancelled():
            self.wait()
            return

        success = True
        if copier.returncode == os.EX_OK:
            try:
                os.unlink(copier.src_path)
            except OSError as e:
                if e.errno not in (errno.ENOENT, errno.ESTALE):
                    logger.error(f"{self.distfile} unlink failed in distfiles: {e}")
                    success = False

        else:
            logger.error(
                ("%s copy from distfiles " "to recycle failed: %s")
                % (self.distfile, copier.future.exception())
            )
            success = False

        if success:
            self._delete_links()
        else:
            self.returncode = 1

        self._current_task = None
        self.wait()

    def _delete_links(self):
        success = True
        for layout in self.config.layouts:
            if isinstance(layout, ContentHashLayout) and not self.distfile.digests:
                logger.debug(f"_delete_links: '{self.distfile}' has no digests")
                continue
            distfile_path = os.path.join(
                self.config.options.distfiles, layout.get_path(self.distfile)
            )
            try:
                os.unlink(distfile_path)
            except OSError as e:
                if e.errno not in (errno.ENOENT, errno.ESTALE):
                    logger.error(f"{self.distfile} unlink failed in distfiles: {e}")
                    success = False

        if success:
            self._success()
            self.returncode = os.EX_OK
        else:
            self.returncode = 1

    def _success(self):
        cpv = "unknown"
        if self.config.distfiles_db is not None:
            cpv = self.config.distfiles_db.get(self.distfile, cpv)

        self.config.delete_count += 1
        self.config.log_success(f"{cpv}\t{self.distfile}\tremoved")

        if self.config.distfiles_db is not None:
            try:
                del self.config.distfiles_db[self.distfile]
            except KeyError:
                pass
            else:
                logger.debug(f"drop '{self.distfile}' from distfiles db")

        if self.config.content_db is not None:
            self.config.content_db.remove(self.distfile)

        if self.config.deletion_db is not None:
            try:
                del self.config.deletion_db[self.distfile]
            except KeyError:
                pass
            else:
                logger.debug(f"drop '{self.distfile}' from deletion db")