From 4356b6fd6596a71ec391165b6996cbfd85d3a4db Mon Sep 17 00:00:00 2001 From: Juan RP Date: Sun, 13 Jul 2014 11:45:04 +0200 Subject: [PATCH] Abort transaction if there's not enough free space on disk. Close #7. --- NEWS | 5 +++++ bin/xbps-install/transaction.c | 18 +++++++++++++----- include/xbps.h.in | 2 ++ lib/transaction_dictionary.c | 20 ++++++++++++++++++++ 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/NEWS b/NEWS index f874d493..1a10a995 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,10 @@ xbps-0.38 (???): + * Before accepting a transaction, xbps now checks if there's enough free space + on the target rootdir (on disk) to proceed with the operation. In code terms, + xbps_transaction_prepare() now returns ENOSPC if the size of the transaction + exceeds the size of the free space. Close issue #7. + * When installing or updating packages, now proper statistics are generated when a binary package from a repository repository exists in the cachedir, and when a package has the "preserve" property. diff --git a/bin/xbps-install/transaction.c b/bin/xbps-install/transaction.c index 9f37ed3b..b884a0e0 100644 --- a/bin/xbps-install/transaction.c +++ b/bin/xbps-install/transaction.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2009-2013 Juan Romero Pardines. + * Copyright (c) 2009-2014 Juan Romero Pardines. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -97,7 +97,7 @@ show_package_list(xbps_object_iterator_t iter, const char *match, int cols) static int show_transaction_sizes(struct transaction *trans, int cols) { - uint64_t dlsize = 0, instsize = 0, rmsize = 0; + uint64_t dlsize = 0, instsize = 0, rmsize = 0, disk_free_size = 0; char size[8]; /* @@ -139,10 +139,10 @@ show_transaction_sizes(struct transaction *trans, int cols) * Show total download/installed/removed size for all required packages. */ xbps_dictionary_get_uint64(trans->d, "total-download-size", &dlsize); - xbps_dictionary_get_uint64(trans->d, "total-installed-size", - &instsize); + xbps_dictionary_get_uint64(trans->d, "total-installed-size", &instsize); xbps_dictionary_get_uint64(trans->d, "total-removed-size", &rmsize); - if (dlsize || instsize || rmsize) + xbps_dictionary_get_uint64(trans->d, "disk-free-size", &disk_free_size); + if (dlsize || instsize || rmsize || disk_free_size) printf("\n"); if (dlsize) { @@ -169,6 +169,14 @@ show_transaction_sizes(struct transaction *trans, int cols) } printf("Size freed on disk: %6s\n", size); } + if (disk_free_size) { + if (xbps_humanize_number(size, (int64_t)disk_free_size) == -1) { + xbps_error_printf("humanize_number3 returns " + "%s\n", strerror(errno)); + return -1; + } + printf("Free space on disk: %6s\n", size); + } printf("\n"); return 0; diff --git a/include/xbps.h.in b/include/xbps.h.in index d81dfc41..8fceb8d3 100644 --- a/include/xbps.h.in +++ b/include/xbps.h.in @@ -1121,6 +1121,8 @@ int xbps_transaction_autoremove_pkgs(struct xbps_handle *xhp); * array of strings object in xhp->transd dictionary). * @retval EAGAIN if there are package conflicts in transaction ("conflicts" * array of strings object in xhp->transd dictionary). + * @retval ENOSPC Not enough free space on target rootdir to continue with the + * transaction. * @retval EINVAL There was an error sorting packages or computing the transaction * sizes. */ diff --git a/lib/transaction_dictionary.c b/lib/transaction_dictionary.c index bcbf3695..cead7bf0 100644 --- a/lib/transaction_dictionary.c +++ b/lib/transaction_dictionary.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "xbps_api_impl.h" @@ -58,6 +59,8 @@ compute_transaction_stats(struct xbps_handle *xhp) xbps_dictionary_t pkg_metad; xbps_object_iterator_t iter; xbps_object_t obj; + struct statvfs svfs; + unsigned long rootdir_free_size; uint64_t tsize, dlsize, instsize, rmsize; uint32_t inst_pkgcnt, up_pkgcnt, cf_pkgcnt, rm_pkgcnt; const char *tract, *pkgver, *repo; @@ -101,6 +104,8 @@ compute_transaction_stats(struct xbps_handle *xhp) !xbps_binpkg_exists(xhp, obj)) { xbps_dictionary_get_uint64(obj, "filename-size", &tsize); + /* signature file: 512 bytes */ + tsize += 512; dlsize += tsize; instsize += tsize; } @@ -159,6 +164,21 @@ compute_transaction_stats(struct xbps_handle *xhp) "total-removed-size", rmsize)) return EINVAL; + /* Get free space from target rootdir: return ENOSPC if there's not enough space */ + if (statvfs(xhp->rootdir, &svfs) == -1) { + xbps_dbg_printf(xhp, "%s: statvfs failed: %s\n", __func__, strerror(errno)); + return EINVAL; + } + /* compute free space on disk */ + rootdir_free_size = svfs.f_bfree * svfs.f_bsize - instsize; + + if (!xbps_dictionary_set_uint64(xhp->transd, + "disk-free-size", rootdir_free_size)) + return EINVAL; + + if (instsize > rootdir_free_size) + return ENOSPC; + return 0; }