diff options
Diffstat (limited to 'lib/libalpm/diskspace.c')
-rw-r--r-- | lib/libalpm/diskspace.c | 209 |
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); |