Commit | Line | Data |
---|---|---|
1479465f GJ |
1 | /* |
2 | * dpkg - main program for package management | |
3 | * infodb-upgrade.c - package control information database format upgrade | |
4 | * | |
5 | * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> | |
6 | * Copyright © 2011-2014 Guillem Jover <guillem@debian.org> | |
7 | * Copyright © 2011 Linaro Limited | |
8 | * Copyright © 2011 Raphaël Hertzog <hertzog@debian.org> | |
9 | * | |
10 | * This is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License as published by | |
12 | * the Free Software Foundation; either version 2 of the License, or | |
13 | * (at your option) any later version. | |
14 | * | |
15 | * This is distributed in the hope that it will be useful, | |
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | * GNU General Public License for more details. | |
19 | * | |
20 | * You should have received a copy of the GNU General Public License | |
21 | * along with this program. If not, see <https://www.gnu.org/licenses/>. | |
22 | */ | |
23 | ||
24 | #include <config.h> | |
25 | #include <compat.h> | |
26 | ||
27 | #include <sys/types.h> | |
28 | #include <sys/stat.h> | |
29 | ||
30 | #include <errno.h> | |
31 | #include <stdlib.h> | |
32 | #include <unistd.h> | |
33 | ||
34 | #include <dpkg/i18n.h> | |
35 | #include <dpkg/dpkg.h> | |
36 | #include <dpkg/dpkg-db.h> | |
37 | #include <dpkg/path.h> | |
38 | #include <dpkg/dir.h> | |
39 | ||
40 | #include "filesdb.h" | |
41 | #include "infodb.h" | |
42 | ||
43 | struct rename_node { | |
44 | struct rename_node *next; | |
45 | char *old; | |
46 | char *new; | |
47 | }; | |
48 | ||
49 | /* Global variables. */ | |
50 | static struct rename_node *rename_head = NULL; | |
51 | ||
52 | static struct rename_node * | |
53 | rename_node_new(const char *old, const char *new, struct rename_node *next) | |
54 | { | |
55 | struct rename_node *node; | |
56 | ||
57 | node = m_malloc(sizeof(*node)); | |
58 | node->next = next; | |
59 | node->old = m_strdup(old); | |
60 | node->new = m_strdup(new); | |
61 | ||
62 | return node; | |
63 | } | |
64 | ||
65 | static void | |
66 | rename_node_free(struct rename_node *node) | |
67 | { | |
68 | free(node->old); | |
69 | free(node->new); | |
70 | free(node); | |
71 | } | |
72 | ||
73 | static void | |
74 | pkg_infodb_link_multiarch_files(void) | |
75 | { | |
76 | DIR *db_dir; | |
77 | struct dirent *db_de; | |
78 | struct varbuf pkgname = VARBUF_INIT; | |
79 | struct varbuf oldname = VARBUF_INIT; | |
80 | struct varbuf newname = VARBUF_INIT; | |
81 | struct varbuf_state db_path_state; | |
82 | ||
83 | varbuf_add_str(&oldname, pkg_infodb_get_dir()); | |
84 | varbuf_add_char(&oldname, '/'); | |
85 | varbuf_end_str(&oldname); | |
86 | varbuf_snapshot(&oldname, &db_path_state); | |
87 | ||
88 | varbuf_add_buf(&newname, oldname.buf, oldname.used); | |
89 | varbuf_end_str(&newname); | |
90 | ||
91 | db_dir = opendir(oldname.buf); | |
92 | if (!db_dir) | |
93 | ohshite(_("cannot read info directory")); | |
94 | ||
95 | push_cleanup(cu_closedir, ~0, NULL, 0, 1, (void *)db_dir); | |
96 | while ((db_de = readdir(db_dir)) != NULL) { | |
97 | const char *filetype, *dot; | |
98 | struct pkginfo *pkg; | |
99 | struct pkgset *set; | |
100 | ||
101 | /* Ignore dotfiles, including ‘.’ and ‘..’. */ | |
102 | if (db_de->d_name[0] == '.') | |
103 | continue; | |
104 | ||
105 | /* Ignore anything odd. */ | |
106 | dot = strrchr(db_de->d_name, '.'); | |
107 | if (dot == NULL) | |
108 | continue; | |
109 | ||
110 | varbuf_reset(&pkgname); | |
111 | varbuf_add_buf(&pkgname, db_de->d_name, dot - db_de->d_name); | |
112 | varbuf_end_str(&pkgname); | |
113 | ||
114 | /* Skip files already converted. */ | |
115 | if (strchr(pkgname.buf, ':')) | |
116 | continue; | |
117 | ||
118 | set = pkg_db_find_set(pkgname.buf); | |
119 | for (pkg = &set->pkg; pkg; pkg = pkg->arch_next) | |
120 | if (pkg->status != PKG_STAT_NOTINSTALLED) | |
121 | break; | |
122 | if (!pkg) { | |
123 | warning(_("info file %s/%s not associated to any package"), | |
124 | pkg_infodb_get_dir(), db_de->d_name); | |
125 | continue; | |
126 | } | |
127 | ||
128 | /* Does it need to be upgraded? */ | |
129 | if (pkg->installed.multiarch != PKG_MULTIARCH_SAME) | |
130 | continue; | |
131 | ||
132 | /* Skip past the full stop. */ | |
133 | filetype = dot + 1; | |
134 | ||
135 | varbuf_rollback(&oldname, &db_path_state); | |
136 | varbuf_add_str(&oldname, db_de->d_name); | |
137 | varbuf_end_str(&oldname); | |
138 | ||
139 | varbuf_rollback(&newname, &db_path_state); | |
140 | varbuf_add_pkgbin_name(&newname, pkg, &pkg->installed, pnaw_always); | |
141 | varbuf_add_char(&newname, '.'); | |
142 | varbuf_add_str(&newname, filetype); | |
143 | varbuf_end_str(&newname); | |
144 | ||
145 | if (link(oldname.buf, newname.buf) && errno != EEXIST) | |
146 | ohshite(_("error creating hard link '%.255s'"), | |
147 | newname.buf); | |
148 | rename_head = rename_node_new(oldname.buf, newname.buf, rename_head); | |
149 | } | |
150 | pop_cleanup(ehflag_normaltidy); /* closedir */ | |
151 | ||
152 | varbuf_destroy(&pkgname); | |
153 | varbuf_destroy(&newname); | |
154 | varbuf_destroy(&oldname); | |
155 | } | |
156 | ||
157 | static void | |
158 | cu_abort_db_upgrade(int argc, void **argv) | |
159 | { | |
160 | struct atomic_file *file = argv[0]; | |
161 | struct rename_node *next; | |
162 | ||
163 | /* Restore the old files if needed and drop the newly created files. */ | |
164 | while (rename_head) { | |
165 | next = rename_head->next; | |
166 | if (link(rename_head->new, rename_head->old) && errno != EEXIST) | |
167 | ohshite(_("error creating hard link '%.255s'"), | |
168 | rename_head->old); | |
169 | if (unlink(rename_head->new)) | |
170 | ohshite(_("cannot remove '%.250s'"), rename_head->new); | |
171 | rename_node_free(rename_head); | |
172 | rename_head = next; | |
173 | } | |
174 | if (unlink(file->name_new) && errno != ENOENT) | |
175 | ohshite(_("cannot remove '%.250s'"), file->name_new); | |
176 | ||
177 | atomic_file_free(file); | |
178 | } | |
179 | ||
180 | static void | |
181 | pkg_infodb_write_format(struct atomic_file *file, int version) | |
182 | { | |
183 | if (fprintf(file->fp, "%d\n", version) < 0) | |
184 | ohshite(_("error while writing '%s'"), file->name_new); | |
185 | ||
186 | atomic_file_sync(file); | |
187 | atomic_file_close(file); | |
188 | dir_sync_path_parent(file->name); | |
189 | ||
190 | pkg_infodb_set_format(version); | |
191 | } | |
192 | ||
193 | static void | |
194 | pkg_infodb_unlink_monoarch_files(void) | |
195 | { | |
196 | struct rename_node *next; | |
197 | ||
198 | while (rename_head) { | |
199 | next = rename_head->next; | |
200 | if (unlink(rename_head->old)) | |
201 | ohshite(_("cannot remove '%.250s'"), rename_head->old); | |
202 | rename_node_free(rename_head); | |
203 | rename_head = next; | |
204 | } | |
205 | } | |
206 | ||
207 | static void | |
208 | pkg_infodb_upgrade_to_multiarch(void) | |
209 | { | |
210 | struct atomic_file *db_file; | |
211 | char *db_format_file; | |
212 | ||
213 | db_format_file = dpkg_db_get_path(INFODIR "/format"); | |
214 | db_file = atomic_file_new(db_format_file, 0); | |
215 | atomic_file_open(db_file); | |
216 | ||
217 | push_cleanup(cu_abort_db_upgrade, ehflag_bombout, NULL, 0, 1, db_file); | |
218 | ||
219 | pkg_infodb_link_multiarch_files(); | |
220 | pkg_infodb_write_format(db_file, 1); | |
221 | ||
222 | pkg_infodb_unlink_monoarch_files(); | |
223 | atomic_file_commit(db_file); | |
224 | dir_sync_path(pkg_infodb_get_dir()); | |
225 | ||
226 | pop_cleanup(ehflag_normaltidy); | |
227 | ||
228 | atomic_file_free(db_file); | |
229 | free(db_format_file); | |
230 | } | |
231 | ||
232 | /** | |
233 | * Upgrade the infodb if there's the need and possibility. | |
234 | * | |
235 | * Currently this implies, that the modstatdb was opened for writing and: | |
236 | * - previous upgrade has not been completed; or | |
237 | * - current format is not the latest one. | |
238 | */ | |
239 | void | |
240 | pkg_infodb_upgrade(void) | |
241 | { | |
242 | enum pkg_infodb_format db_format; | |
243 | ||
244 | /* Make sure to always read and verify the format version. */ | |
245 | db_format = pkg_infodb_get_format(); | |
246 | ||
247 | if (modstatdb_get_status() < msdbrw_write) | |
248 | return; | |
249 | ||
250 | if (db_format < PKG_INFODB_FORMAT_MULTIARCH || | |
251 | pkg_infodb_is_upgrading()) | |
252 | pkg_infodb_upgrade_to_multiarch(); | |
253 | } |