summaryrefslogtreecommitdiff
path: root/lib/libalpm/diskspace.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libalpm/diskspace.c')
-rw-r--r--lib/libalpm/diskspace.c209
1 files changed, 155 insertions, 54 deletions
diff --git a/lib/libalpm/diskspace.c b/lib/libalpm/diskspace.c
index 45908b22..2582c4c3 100644
--- a/lib/libalpm/diskspace.c
+++ b/lib/libalpm/diskspace.c
@@ -17,8 +17,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "config.h"
-
#include <stdio.h>
#include <errno.h>
@@ -60,6 +58,37 @@ static int mount_point_cmp(const void *p1, const void *p2)
return -strcmp(mp1->mount_dir, mp2->mount_dir);
}
+static void mount_point_list_free(alpm_list_t *mount_points)
+{
+ alpm_list_t *i;
+
+ for(i = mount_points; i; i = i->next) {
+ alpm_mountpoint_t *data = i->data;
+ FREE(data->mount_dir);
+ }
+ FREELIST(mount_points);
+}
+
+static int mount_point_load_fsinfo(alpm_handle_t *handle, alpm_mountpoint_t *mountpoint)
+{
+#if defined(HAVE_GETMNTENT)
+ /* grab the filesystem usage */
+ if(statvfs(mountpoint->mount_dir, &(mountpoint->fsp)) != 0) {
+ _alpm_log(handle, ALPM_LOG_WARNING,
+ _("could not get filesystem information for %s: %s\n"),
+ mountpoint->mount_dir, strerror(errno));
+ mountpoint->fsinfo_loaded = MOUNT_FSINFO_FAIL;
+ return -1;
+ }
+
+ _alpm_log(handle, ALPM_LOG_DEBUG, "loading fsinfo for %s\n", mountpoint->mount_dir);
+ mountpoint->read_only = mountpoint->fsp.f_flag & ST_RDONLY;
+ mountpoint->fsinfo_loaded = MOUNT_FSINFO_LOADED;
+#endif
+
+ return 0;
+}
+
static alpm_list_t *mount_point_list(alpm_handle_t *handle)
{
alpm_list_t *mount_points = NULL, *ptr;
@@ -73,28 +102,15 @@ static alpm_list_t *mount_point_list(alpm_handle_t *handle)
fp = setmntent(MOUNTED, "r");
if(fp == NULL) {
+ _alpm_log(handle, ALPM_LOG_ERROR, _("could not open file: %s: %s\n"),
+ MOUNTED, strerror(errno));
return NULL;
}
while((mnt = getmntent(fp))) {
- struct statvfs fsp;
- if(!mnt) {
- _alpm_log(handle, ALPM_LOG_WARNING,
- _("could not get filesystem information\n"));
- continue;
- }
- if(statvfs(mnt->mnt_dir, &fsp) != 0) {
- _alpm_log(handle, ALPM_LOG_WARNING,
- _("could not get filesystem information for %s: %s\n"),
- mnt->mnt_dir, strerror(errno));
- continue;
- }
-
CALLOC(mp, 1, sizeof(alpm_mountpoint_t), RET_ERR(handle, ALPM_ERR_MEMORY, NULL));
mp->mount_dir = strdup(mnt->mnt_dir);
mp->mount_dir_len = strlen(mp->mount_dir);
- memcpy(&(mp->fsp), &fsp, sizeof(struct statvfs));
- mp->read_only = fsp.f_flag & ST_RDONLY;
mount_points = alpm_list_add(mount_points, mp);
}
@@ -109,23 +125,15 @@ static alpm_list_t *mount_point_list(alpm_handle_t *handle)
fp = fopen("/etc/mnttab", "r");
if(fp == NULL) {
+ _alpm_log(handle, ALPM_LOG_ERROR, _("could not open file %s: %s\n"),
+ "/etc/mnttab", strerror(errno));
return NULL;
}
while((ret = getmntent(fp, &mnt)) == 0) {
- struct statvfs fsp;
- if(statvfs(mnt->mnt_mountp, &fsp) != 0) {
- _alpm_log(handle, ALPM_LOG_WARNING,
- _("could not get filesystem information for %s: %s\n"),
- mnt->mnt_mountp, strerror(errno));
- continue;
- }
-
CALLOC(mp, 1, sizeof(alpm_mountpoint_t), RET_ERR(handle, ALPM_ERR_MEMORY, NULL));
mp->mount_dir = strdup(mnt->mnt_mountp);
mp->mount_dir_len = strlen(mp->mount_dir);
- memcpy(&(mp->fsp), &fsp, sizeof(struct statvfs));
- mp->read_only = fsp.f_flag & ST_RDONLY;
mount_points = alpm_list_add(mount_points, mp);
}
@@ -144,6 +152,8 @@ static alpm_list_t *mount_point_list(alpm_handle_t *handle)
entries = getmntinfo(&fsp, MNT_NOWAIT);
if(entries < 0) {
+ _alpm_log(handle, ALPM_LOG_ERROR,
+ _("could not get filesystem information\n"));
return NULL;
}
@@ -158,6 +168,9 @@ static alpm_list_t *mount_point_list(alpm_handle_t *handle)
mp->read_only = fsp->f_flags & MNT_RDONLY;
#endif
+ /* we don't support lazy loading on this platform */
+ mp->fsinfo_loaded = MOUNT_FSINFO_LOADED;
+
mount_points = alpm_list_add(mount_points, mp);
}
#endif
@@ -166,7 +179,7 @@ static alpm_list_t *mount_point_list(alpm_handle_t *handle)
mount_point_cmp);
for(ptr = mount_points; ptr != NULL; ptr = ptr->next) {
mp = ptr->data;
- _alpm_log(handle, ALPM_LOG_DEBUG, "mountpoint: %s\n", mp->mount_dir);
+ _alpm_log(handle, ALPM_LOG_DEBUG, "discovered mountpoint: %s\n", mp->mount_dir);
}
return mount_points;
}
@@ -215,6 +228,7 @@ static int calculate_removed_size(alpm_handle_t *handle,
alpm_mountpoint_t *mp;
struct stat st;
char path[PATH_MAX];
+ blkcnt_t remove_size;
const char *filename = file->name;
snprintf(path, PATH_MAX, "%s%s", handle->root, filename);
@@ -233,9 +247,21 @@ static int calculate_removed_size(alpm_handle_t *handle,
continue;
}
+ /* don't check a mount that we know we can't stat */
+ if(mp && mp->fsinfo_loaded == MOUNT_FSINFO_FAIL) {
+ continue;
+ }
+
+ /* lazy load filesystem info */
+ if(mp->fsinfo_loaded == MOUNT_FSINFO_UNLOADED) {
+ if(mount_point_load_fsinfo(handle, mp) < 0) {
+ continue;
+ }
+ }
+
/* the addition of (divisor - 1) performs ceil() with integer division */
- mp->blocks_needed -=
- (st.st_size + mp->fsp.f_bsize - 1) / mp->fsp.f_bsize;
+ remove_size = (st.st_size + mp->fsp.f_bsize - 1) / mp->fsp.f_bsize;
+ mp->blocks_needed -= remove_size;
mp->used |= USED_REMOVE;
}
@@ -256,6 +282,7 @@ static int calculate_installed_size(alpm_handle_t *handle,
const alpm_file_t *file = filelist->files + i;
alpm_mountpoint_t *mp;
char path[PATH_MAX];
+ blkcnt_t install_size;
const char *filename = file->name;
/* libarchive reports these as zero size anyways */
@@ -279,15 +306,108 @@ static int calculate_installed_size(alpm_handle_t *handle,
continue;
}
+ /* don't check a mount that we know we can't stat */
+ if(mp && mp->fsinfo_loaded == MOUNT_FSINFO_FAIL) {
+ continue;
+ }
+
+ /* lazy load filesystem info */
+ if(mp->fsinfo_loaded == MOUNT_FSINFO_UNLOADED) {
+ if(mount_point_load_fsinfo(handle, mp) < 0) {
+ continue;
+ }
+ }
+
/* the addition of (divisor - 1) performs ceil() with integer division */
- mp->blocks_needed +=
- (file->size + mp->fsp.f_bsize - 1) / mp->fsp.f_bsize;
+ install_size = (file->size + mp->fsp.f_bsize - 1) / mp->fsp.f_bsize;
+ mp->blocks_needed += install_size;
mp->used |= USED_INSTALL;
}
return 0;
}
+static int check_mountpoint(alpm_handle_t *handle, alpm_mountpoint_t *mp)
+{
+ /* cushion is roughly min(5% capacity, 20MiB) */
+ fsblkcnt_t fivepc = (mp->fsp.f_blocks / 20) + 1;
+ fsblkcnt_t twentymb = (20 * 1024 * 1024 / mp->fsp.f_bsize) + 1;
+ fsblkcnt_t cushion = fivepc < twentymb ? fivepc : twentymb;
+ blkcnt_t needed = mp->max_blocks_needed + cushion;
+
+ _alpm_log(handle, ALPM_LOG_DEBUG,
+ "partition %s, needed %jd, cushion %ju, free %ju\n",
+ mp->mount_dir, (intmax_t)mp->max_blocks_needed,
+ (uintmax_t)cushion, (uintmax_t)mp->fsp.f_bfree);
+ if(needed >= 0 && (fsblkcnt_t)needed > mp->fsp.f_bfree) {
+ _alpm_log(handle, ALPM_LOG_ERROR,
+ _("Partition %s too full: %jd blocks needed, %jd blocks free\n"),
+ mp->mount_dir, (intmax_t)needed, (uintmax_t)mp->fsp.f_bfree);
+ return 1;
+ }
+ return 0;
+}
+
+int _alpm_check_downloadspace(alpm_handle_t *handle, const char *cachedir,
+ size_t num_files, off_t *file_sizes)
+{
+ alpm_list_t *mount_points;
+ alpm_mountpoint_t *cachedir_mp;
+ char resolved_cachedir[PATH_MAX];
+ size_t j;
+ int error = 0;
+
+ /* resolve the cachedir path to ensure we check the right mountpoint. We
+ * handle failures silently, and continue to use the possibly unresolved
+ * path. */
+ if(realpath(cachedir, resolved_cachedir) != NULL) {
+ cachedir = resolved_cachedir;
+ }
+
+ mount_points = mount_point_list(handle);
+ if(mount_points == NULL) {
+ _alpm_log(handle, ALPM_LOG_ERROR, _("could not determine filesystem mount points\n"));
+ return -1;
+ }
+
+ cachedir_mp = match_mount_point(mount_points, cachedir);
+ if(cachedir_mp == NULL) {
+ _alpm_log(handle, ALPM_LOG_ERROR, _("could not determine cachedir mount point %s\n"),
+ cachedir);
+ error = 1;
+ goto finish;
+ }
+
+ if(cachedir_mp->fsinfo_loaded == MOUNT_FSINFO_UNLOADED) {
+ if(mount_point_load_fsinfo(handle, cachedir_mp)) {
+ error = 1;
+ goto finish;
+ }
+ }
+
+ /* there's no need to check for a R/O mounted filesystem here, as
+ * _alpm_filecache_setup will never give us a non-writable directory */
+
+ /* round up the size of each file to the nearest block and accumulate */
+ for(j = 0; j < num_files; j++) {
+ cachedir_mp->max_blocks_needed += (file_sizes[j] + cachedir_mp->fsp.f_bsize + 1) /
+ cachedir_mp->fsp.f_bsize;
+ }
+
+ if(check_mountpoint(handle, cachedir_mp)) {
+ error = 1;
+ }
+
+finish:
+ mount_point_list_free(mount_points);
+
+ if(error) {
+ RET_ERR(handle, ALPM_ERR_DISK_SPACE, -1);
+ }
+
+ return 0;
+}
+
int _alpm_check_diskspace(alpm_handle_t *handle)
{
alpm_list_t *mount_points, *i;
@@ -356,32 +476,13 @@ int _alpm_check_diskspace(alpm_handle_t *handle)
_alpm_log(handle, ALPM_LOG_ERROR, _("Partition %s is mounted read only\n"),
data->mount_dir);
error = 1;
- } else if(data->used & USED_INSTALL) {
- /* cushion is roughly min(5% capacity, 20MiB) */
- fsblkcnt_t fivepc = (data->fsp.f_blocks / 20) + 1;
- fsblkcnt_t twentymb = (20 * 1024 * 1024 / data->fsp.f_bsize) + 1;
- fsblkcnt_t cushion = fivepc < twentymb ? fivepc : twentymb;
- blkcnt_t needed = data->max_blocks_needed + cushion;
-
- _alpm_log(handle, ALPM_LOG_DEBUG,
- "partition %s, needed %jd, cushion %ju, free %ju\n",
- data->mount_dir, (intmax_t)data->max_blocks_needed,
- (uintmax_t)cushion, (uintmax_t)data->fsp.f_bfree);
- if(needed >= 0 && (fsblkcnt_t)needed > data->fsp.f_bfree) {
- _alpm_log(handle, ALPM_LOG_ERROR,
- _("Partition %s too full: %jd blocks needed, %jd blocks free\n"),
- data->mount_dir, (intmax_t)needed, (uintmax_t)data->fsp.f_bfree);
- error = 1;
- }
+ } else if(data->used & USED_INSTALL && check_mountpoint(handle, data)) {
+ error = 1;
}
}
finish:
- for(i = mount_points; i; i = i->next) {
- alpm_mountpoint_t *data = i->data;
- FREE(data->mount_dir);
- }
- FREELIST(mount_points);
+ mount_point_list_free(mount_points);
if(error) {
RET_ERR(handle, ALPM_ERR_DISK_SPACE, -1);