Skip to content

Commit 62925aa

Browse files
committed
feat: block package removal in trans_prepare when dependents are
installed
1 parent d267669 commit 62925aa

4 files changed

Lines changed: 101 additions & 3 deletions

File tree

include/apg/transaction.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ typedef enum
5151
TRANS_ERR_ALREADY_COMMITTED, /**< trans_commit() called more than once. */
5252
TRANS_ERR_INSTALL_FAILED, /**< One or more packages failed to install. */
5353
TRANS_ERR_UNSIGNED, /**< Package rejected by signature policy. */
54+
TRANS_ERR_HAS_DEPENDENTS, /**< Removal blocked by installed dependents. */
5455
} trans_error_t;
5556

5657
/**
@@ -77,6 +78,18 @@ struct trans_conflict
7778
char *conflicts_with; /**< Existing package that conflicts with it. */
7879
};
7980

81+
/**
82+
* @brief A removal blocked by installed dependents.
83+
*
84+
* The array is owned by the transaction and valid until trans_free().
85+
*/
86+
struct trans_blocked_remove
87+
{
88+
char *pkg_name; /**< Package that cannot be removed. */
89+
char **dependents; /**< Names of packages that depend on it. */
90+
int dependent_count;
91+
};
92+
8093
/**
8194
* @brief Opaque transaction handle.
8295
*/
@@ -185,6 +198,20 @@ const struct trans_step *trans_get_plan(const struct apg_trans *trans,
185198
const struct trans_conflict *trans_get_conflicts(const struct apg_trans *trans,
186199
size_t *count);
187200

201+
/**
202+
* @brief Retrieve removals blocked by installed dependents.
203+
*
204+
* Valid after a trans_prepare() call that returned
205+
* @ref TRANS_ERR_HAS_DEPENDENTS. The returned array is owned by the
206+
* transaction and valid until trans_free().
207+
*
208+
* @param trans Transaction after trans_prepare().
209+
* @param count Output parameter set to the number of blocked removes.
210+
* @return Pointer to the first entry, or NULL if none.
211+
*/
212+
const struct trans_blocked_remove *
213+
trans_get_blocked_removes(const struct apg_trans *trans, size_t *count);
214+
188215
/**
189216
* @brief Execute the prepared plan.
190217
*

src/transaction/prepare.c

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -255,16 +255,65 @@ trans_prepare(struct apg_trans *trans)
255255

256256
for (size_t i = 0; i < trans->remove_count; i++)
257257
{
258-
trans_error_t perr = plan_push(
259-
trans, TRANS_OP_REMOVE, trans->remove_names[i], NULL, true, NULL);
258+
const char *name = trans->remove_names[i];
259+
260+
int dep_count = 0;
261+
char **deps = db_get_dependents(trans->db, name, &dep_count);
262+
if (deps && dep_count > 0)
263+
{
264+
if (!trans->blocked_removes)
265+
{
266+
trans->blocked_removes =
267+
malloc(4 * sizeof(*trans->blocked_removes));
268+
if (!trans->blocked_removes)
269+
{
270+
free(deps);
271+
ret = TRANS_ERR_NOMEM;
272+
goto cleanup;
273+
}
274+
trans->blocked_remove_cap = 4;
275+
}
276+
else if (trans->blocked_remove_count == trans->blocked_remove_cap)
277+
{
278+
size_t new_cap = trans->blocked_remove_cap * 2;
279+
struct trans_blocked_remove *tmp =
280+
realloc(trans->blocked_removes, new_cap * sizeof(*tmp));
281+
if (!tmp)
282+
{
283+
free(deps);
284+
ret = TRANS_ERR_NOMEM;
285+
goto cleanup;
286+
}
287+
trans->blocked_removes = tmp;
288+
trans->blocked_remove_cap = new_cap;
289+
}
290+
291+
struct trans_blocked_remove *br =
292+
&trans->blocked_removes[trans->blocked_remove_count++];
293+
br->pkg_name = strdup(name);
294+
br->dependents = deps;
295+
br->dependent_count = dep_count;
296+
if (!br->pkg_name)
297+
{
298+
ret = TRANS_ERR_NOMEM;
299+
goto cleanup;
300+
}
301+
continue;
302+
}
303+
free(deps);
304+
305+
trans_error_t perr =
306+
plan_push(trans, TRANS_OP_REMOVE, name, NULL, true, NULL);
260307
if (perr != TRANS_OK)
261308
{
262309
ret = perr;
263310
goto cleanup;
264311
}
265312
}
266313

267-
if (trans->conflict_count > 0)
314+
if (trans->blocked_remove_count > 0)
315+
ret = TRANS_ERR_HAS_DEPENDENTS;
316+
else if (trans->conflict_count > 0)
268317
ret = TRANS_ERR_CONFLICT;
269318
else
270319
trans->prepared = true;

src/transaction/trans_priv.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ struct apg_trans
3333
size_t conflict_count;
3434
size_t conflict_cap;
3535

36+
struct trans_blocked_remove *blocked_removes;
37+
size_t blocked_remove_count;
38+
size_t blocked_remove_cap;
39+
3640
bool prepared;
3741
bool committed;
3842

src/transaction/transaction.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,15 @@ trans_free(struct apg_trans *trans)
9393
}
9494
free(trans->conflicts);
9595

96+
for (size_t i = 0; i < trans->blocked_remove_count; i++)
97+
{
98+
free(trans->blocked_removes[i].pkg_name);
99+
for (int j = 0; j < trans->blocked_removes[i].dependent_count; j++)
100+
free(trans->blocked_removes[i].dependents[j]);
101+
free(trans->blocked_removes[i].dependents);
102+
}
103+
free(trans->blocked_removes);
104+
96105
free(trans);
97106
}
98107

@@ -170,6 +179,15 @@ trans_get_plan(const struct apg_trans *trans, size_t *count)
170179
return trans->plan;
171180
}
172181

182+
const struct trans_blocked_remove *
183+
trans_get_blocked_removes(const struct apg_trans *trans, size_t *count)
184+
{
185+
if (!trans || !count)
186+
return NULL;
187+
*count = trans->blocked_remove_count;
188+
return trans->blocked_removes;
189+
}
190+
173191
const struct trans_conflict *
174192
trans_get_conflicts(const struct apg_trans *trans, size_t *count)
175193
{

0 commit comments

Comments
 (0)