/
githubmirror
/
lvm2
Обзор
Документация
Войти
/
githubmirror
/
lvm2
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
lib/device/filesystem.c
730 строк
19 KB
Zdenek Kabelac
analyzer: annotate nonnull args and guard unchecked pointers
22 июн 2026, 22:09
22 июн 2026, 22:09
dcdf2a0
Код
Авторство
О чём код?
/* * Copyright (C) 2022 Red Hat, Inc. All rights reserved. * * This file is part of LVM2. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU Lesser General Public License v.2.1. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "base/memory/zalloc.h" #include "lib/misc/lib.h" #include "lib/device/dev-type.h" #include "lib/device/filesystem.h" #include "lib/display/display.h" #include "lib/misc/lvm-exec.h" #include "lib/activate/dev_manager.h" #include "lib/commands/toolcontext.h" #include "lib/config/config.h" #include <dirent.h> #include <mntent.h> #include <sys/ioctl.h> #include <linux/types.h> static const char *_get_lvresize_fs_helper_path(struct cmd_context *cmd) { const char *path = getenv("LVRESIZE_FS_HELPER_PATH"); if (!path) path = find_config_tree_str(cmd, global_lvresize_fs_helper_executable_CFG, NULL); return path; } /* * Set the path of the dm-crypt device, i.e. /dev/dm-N, that is using the LV. */ static int _get_crypt_path(dev_t lv_devt, char *lv_path, char *crypt_path) { char holders_path[PATH_MAX]; char *holder_name; DIR *dr; struct dirent *de; int ret = 0; if (dm_snprintf(holders_path, sizeof(holders_path), "%sdev/block/%u:%u/holders", dm_sysfs_dir(), MAJOR(lv_devt), MINOR(lv_devt)) < 0) { log_error("Couldn't create holder path for %s.", lv_path); return 0; } /* If the crypt dev is not active, there will be no LV holder. */ if (!(dr = opendir(holders_path))) { if (errno == ENOENT) log_error("Missing %s for %s.", holders_path, lv_path); else log_error("Cannot open %s.", holders_path); return 0; } while ((de = readdir(dr))) { if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; holder_name = de->d_name; if (strncmp(holder_name, "dm", 2)) { log_error("Unrecognized holder %s of %s", holder_name, lv_path); ret = 0; break; } /* We could read the holder's dm uuid to verify it's a crypt dev. */ if (dm_snprintf(crypt_path, PATH_MAX, "/dev/%s", holder_name) < 0) { ret = 0; stack; break; } ret = 1; break; } if (closedir(dr)) log_sys_debug("closedir", holders_path); if (ret) log_debug("Found holder %s of %s.", crypt_path, lv_path); else log_debug("No holder in %s", holders_path); return ret; } int lv_crypt_is_active(struct cmd_context *cmd, char *lv_path) { char crypt_path[PATH_MAX] = { 0 }; struct stat st_lv; if (stat(lv_path, &st_lv) < 0) { log_error("Failed to get LV path %s", lv_path); return 0; } return _get_crypt_path(st_lv.st_rdev, lv_path, crypt_path); } static int _fs_get_mnt(struct fs_info *fsi, dev_t devt) { struct stat stme; FILE *fme = NULL; struct mntent *me; /* * st_dev of mnt_dir in btrfs is an anonymous device number, * use mnt_fsname and st_rdev instead. */ int is_btrfs = !strcmp(fsi->fstype, "btrfs"); /* * Note: used swap devices are not considered as mount points, * hence they're not listed in /etc/mtab, we'd need to read the * /proc/swaps instead. We don't need it at this moment though, * but if we do once, read the /proc/swaps here if fsi->fstype == "swap". */ if (!(fme = setmntent("/etc/mtab", "r"))) return_0; while ((me = getmntent(fme))) { const char *stat_path = is_btrfs ? me->mnt_fsname : me->mnt_dir; if (strcmp(me->mnt_type, fsi->fstype)) continue; if (!me->mnt_dir || me->mnt_dir[0] != '/') continue; if (!me->mnt_fsname || me->mnt_fsname[0] != '/') continue; if (stat(stat_path, &stme) < 0) { log_sys_error("stat", stat_path); continue; } if ((is_btrfs ? stme.st_rdev : stme.st_dev) != devt) continue; fsi->mounted = 1; log_debug("fs_get_info %s is mounted \"%s\"", fsi->fs_dev_path, me->mnt_dir); dm_strncpy(fsi->mount_dir, me->mnt_dir, sizeof(fsi->mount_dir)); break; /* found mount */ } endmntent(fme); return 1; } static int _btrfs_get_mnt(struct fs_info *fsi, dev_t lv_devt) { char devices_path[PATH_MAX]; char rdev_path[PATH_MAX]; unsigned major, minor; dev_t devt; char buffer[16]; char *device_name; DIR *dr; struct dirent *de; int ret = 1; int fd = -1; ssize_t r; /* For a mounted btrfs, there will be a sys dir like /sys/fs/btrfs/$uuid/devices */ if (dm_snprintf(devices_path, sizeof(devices_path), "%sfs/btrfs/%s/devices", dm_sysfs_dir(), fsi->uuid) < 0) { log_error("Couldn't create btrfs devices path for %s.", fsi->fs_dev_path); return 0; } /* btrfs module is not available or the device is not mounted */ if (!(dr = opendir(devices_path))) { if (errno == ENOENT) { fsi->mounted = 0; return 1; } log_sys_error("opendir", devices_path); return 0; } /* * Here iterates entries under /sys/fs/btrfs/$uuid/devices and read devt. * There is only one mnt entry per mounted fs even it's a multi-devices fs. * So also call _fs_get_mnt for every devices to find a matched mount point. */ while ((de = readdir(dr))) { if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; device_name = de->d_name; if (dm_snprintf(rdev_path, sizeof(rdev_path), "%s/%s/dev", devices_path, device_name) < 0) { log_error("Couldn't create rdev path for %s.", fsi->fs_dev_path); ret = 0; break; } if ((fd = open(rdev_path, O_RDONLY)) < 0) { log_sys_debug("open", rdev_path); ret = 0; break; } r = read(fd, buffer, sizeof(buffer)); if (r <= 0) { ret = 0; log_sys_debug("read", rdev_path); } if (close(fd)) log_sys_debug("close", rdev_path); fd = -1; if (r <= 0) break; buffer[r - 1] = 0; if (sscanf(buffer, "%u:%u", &major, &minor) != 2) { ret = 0; log_sys_debug("sscanf", rdev_path); break; } devt = MKDEV(major, minor); if (devt == lv_devt) fsi->mounted = 1; if (fsi->mount_dir[0] == 0) _fs_get_mnt(fsi, devt); if (fsi->mounted && fsi->mount_dir[0]) break; } if (closedir(dr)) log_sys_debug("closedir", devices_path); if (fsi->mounted && fsi->mount_dir[0] == 0) { log_error("Couldn't get mount point for %s.", fsi->fs_dev_path); ret = 0; } return ret; } int fs_get_info(struct cmd_context *cmd, struct logical_volume *lv, struct fs_info *fsi) { char lv_path[PATH_MAX]; char crypt_path[PATH_MAX] = { 0 }; struct stat st_lv; struct stat st_crypt; struct stat st_top; struct fs_info info; int fd; int ret; if (dm_snprintf(lv_path, PATH_MAX, "%s%s/%s", lv->vg->cmd->dev_dir, lv->vg->name, lv->name) < 0) { log_error("Couldn't create LV path for %s.", display_lvname(lv)); return 0; } if (stat(lv_path, &st_lv) < 0) { log_error("Failed to get LV path %s", lv_path); return 0; } memset(&info, 0, sizeof(info)); if (!fs_get_blkid(lv_path, &info)) { log_error("No file system info from blkid for %s", display_lvname(lv)); return 0; } if (info.nofs) { fsi->nofs = 1; return 1; } /* * If there's a LUKS dm-crypt layer over the LV, then * return fs info from that layer, setting needs_crypt * to indicate a crypt layer between the fs and LV. */ if (!strcmp(info.fstype, "crypto_LUKS")) { if (!_get_crypt_path(st_lv.st_rdev, lv_path, crypt_path)) { log_error("Cannot find active LUKS dm-crypt device using %s.", display_lvname(lv)); return 0; } memset(&info, 0, sizeof(info)); log_print_unless_silent("Checking crypt device %s on LV %s.", crypt_path, display_lvname(lv)); if ((fd = open(crypt_path, O_RDONLY)) < 0) { log_error("Failed to open crypt path %s.", crypt_path); return 0; } if ((ret = fstat(fd, &st_crypt)) < 0) log_sys_error("fstat", crypt_path); else if ((ret = ioctl(fd, BLKGETSIZE64, &info.crypt_dev_size_bytes)) < 0) log_error("Failed to get crypt device size %s.", crypt_path); if (close(fd)) log_sys_debug("close", crypt_path); if (ret < 0) return 0; if (!fs_get_blkid(crypt_path, &info)) { log_error("No file system info from blkid for dm-crypt device %s on LV %s.", crypt_path, display_lvname(lv)); return 0; } *fsi = info; fsi->needs_crypt = 1; fsi->crypt_devt = st_crypt.st_rdev; memcpy(fsi->fs_dev_path, crypt_path, PATH_MAX); st_top = st_crypt; if (!get_crypt_table_offset(st_crypt.st_rdev, &fsi->crypt_offset_bytes)) { log_error("Failed to get crypt data offset."); return 0; } } else { *fsi = info; memcpy(fsi->fs_dev_path, lv_path, PATH_MAX); st_top = st_lv; } if (!strcmp(fsi->fstype, "btrfs")) ret = _btrfs_get_mnt(fsi, st_lv.st_rdev); else ret = _fs_get_mnt(fsi, st_top.st_rdev); /* blkid FSLASTBLOCK may be incorrect for mounted xfs */ if (fsi->mounted && !strcmp(fsi->fstype, "xfs")) { if (!(ret = fs_xfs_update_size_mounted(cmd, lv, lv_path, fsi))) stack; } fsi->unmounted = !fsi->mounted; return ret; } int fs_mount_state_is_misnamed(struct cmd_context *cmd, struct logical_volume *lv, char *lv_path, char *fstype) { FILE *fp; char proc_line[PATH_MAX]; char proc_fstype[FSTYPE_MAX + 1]; char proc_devpath[PATH_MAX + 1]; char proc_mntpath[PATH_MAX + 1]; char mtab_mntpath[PATH_MAX] = { 0 }; char dm_devpath[PATH_MAX]; char dm_devpath_resolved[PATH_MAX]; char proc_devpath_resolved[PATH_MAX]; char tmp_path[PATH_MAX]; char *dm_name; struct stat st_lv; struct stat stme; FILE *fme = NULL; struct mntent *me; int renamed = 0; int dev_match, dir_match; if (stat(lv_path, &st_lv) < 0) { log_error("Failed to get LV path %s", lv_path); return 0; } /* * If LVs have been renamed while their file systems were mounted, then * inconsistencies appear in the device path and mount point info * provided by getmntent and /proc/mounts. If there's any * inconsistency or duplication of info for the LV name or the mount * point, then give up and don't try fs resize which is likely to fail * due to kernel problems where mounts reference old device names * causing fs resizing tools to fail. */ if (!(fme = setmntent("/etc/mtab", "r"))) return_0; while ((me = getmntent(fme))) { if (strcmp(me->mnt_type, fstype)) continue; if (!me->mnt_dir || me->mnt_dir[0] != '/') continue; if (!me->mnt_fsname || me->mnt_fsname[0] != '/') continue; if (stat(me->mnt_dir, &stme) < 0) continue; if (stme.st_dev != st_lv.st_rdev) continue; if (!_dm_strncpy(mtab_mntpath, me->mnt_dir, sizeof(mtab_mntpath))) continue; /* Ignore too long unsupported paths */ break; } endmntent(fme); if (mtab_mntpath[0]) log_debug("%s mtab mntpath %s", display_lvname(lv), mtab_mntpath); /* * In mtab dir path, replace each ascii space character with the * four characters \040 which is how /proc/mounts represents spaces. * The mnt dir from /etc/mtab and /proc/mounts are compared below. */ if (strchr(mtab_mntpath, ' ')) { unsigned i, j = 0; memcpy(tmp_path, mtab_mntpath, sizeof(tmp_path)); memset(mtab_mntpath, 0, sizeof(mtab_mntpath)); for (i = 0; i < sizeof(tmp_path) && tmp_path[i]; i++) { if (tmp_path[i] == ' ') { if (j + 4 >= sizeof(mtab_mntpath)) break; mtab_mntpath[j++] = '\\'; mtab_mntpath[j++] = '0'; mtab_mntpath[j++] = '4'; mtab_mntpath[j++] = '0'; continue; } if (j + 1 >= sizeof(mtab_mntpath)) break; mtab_mntpath[j++] = tmp_path[i]; } } if (!(dm_name = dm_build_dm_name(cmd->mem, lv->vg->name, lv->name, NULL))) return_0; if ((dm_snprintf(dm_devpath, sizeof(dm_devpath), "%s/%s", dm_dir(), dm_name) < 0)) return_0; if (!(fp = fopen("/proc/mounts", "r"))) return_0; while (fgets(proc_line, sizeof(proc_line), fp)) { if (proc_line[0] != '/') continue; if (sscanf(proc_line, "%" DM_TO_STRING(PATH_MAX) "s %" DM_TO_STRING(PATH_MAX) "s %" DM_TO_STRING(FSTYPE_MAX) "s", proc_devpath, proc_mntpath, proc_fstype) != 3) continue; if (strcmp(fstype, proc_fstype)) continue; /* * When an LV is mounted on two dirs, it appears in /proc/mounts twice as * /dev/mapper/vg-lvol0 on /foo type xfs ... * /dev/mapper/vg-lvol0 on /bar type xfs ... * All entries match dm_devpath, one entry matches mntpath, * and other entries don't match mntpath. * * When an LV is mounted on one dir, and is renamed from lvol0 to lvol1, * it appears in /proc/mounts once as * /dev/mapper/vg-lvol0 on /foo type xfs ... */ dir_match = !strcmp(mtab_mntpath, proc_mntpath); /* * Resolve symlinks before comparing device paths. In test environments, * dm_devpath may be a symlink (e.g., /dev/shm/LVMTEST/dev/mapper/vg-lv * -> /dev/mapper/vg-lv), while proc_devpath is the resolved real path. * Compare resolved paths to avoid false positives for rename detection. */ if (realpath(dm_devpath, dm_devpath_resolved) && realpath(proc_devpath, proc_devpath_resolved)) dev_match = !strcmp(dm_devpath_resolved, proc_devpath_resolved); else dev_match = !strcmp(dm_devpath, proc_devpath); if (!dir_match && !dev_match) continue; if (dev_match && !dir_match) { log_debug("LV %s mounted at %s also mounted at %s.", dm_devpath, mtab_mntpath, proc_mntpath); continue; } if (!dev_match && dir_match) { log_error("LV %s mounted at %s may have been renamed (from %s).", dm_devpath, proc_mntpath, proc_devpath); renamed = 1; } } if (fclose(fp)) stack; if (renamed) { log_error("File system resizing not supported: fs utilities do not support renamed devices."); return 1; } return 0; } #define FS_CMD_MAX_ARGS 16 int crypt_resize_script(struct cmd_context *cmd, struct logical_volume *lv, struct fs_info *fsi) { char crypt_path[PATH_MAX]; char newsize_str[16] = { 0 }; const char *argv[FS_CMD_MAX_ARGS + 4]; int args = 0; int status; if (dm_snprintf(newsize_str, sizeof(newsize_str), "%llu", (unsigned long long)fsi->new_size_bytes) < 0) return_0; if (dm_snprintf(crypt_path, sizeof(crypt_path), "/dev/dm-%u", MINOR(fsi->crypt_devt)) < 0) return_0; argv[0] = _get_lvresize_fs_helper_path(cmd); argv[++args] = "--cryptresize"; argv[++args] = "--cryptpath"; argv[++args] = crypt_path; argv[++args] = "--newsizebytes"; argv[++args] = newsize_str; argv[++args] = NULL; if (!exec_cmd(cmd, argv, &status, 1)) { log_error("Failed to resize crypt dev with lvresize_fs_helper."); return 0; } return 1; } /* * The helper script does the following steps for reduce: * devpath = $cryptpath ? $cryptpath : $lvpath * if needs_unmount * umount $mountdir * if needs_fsck * e2fsck -f -p $devpath * if needs_mount * mount $devpath $tmpdir * if $fstype == "ext" * resize2fs $devpath $newsize_kb * if needs_crypt * cryptsetup resize --size $newsize_sectors $cryptpath * * Note: when a crypt layer is included, new_size_bytes is smaller * than newsize_bytes_lv because of the crypt header. */ int fs_reduce_script(struct cmd_context *cmd, struct logical_volume *lv, struct fs_info *fsi, char *fsmode) { char lv_path[PATH_MAX]; char crypt_path[PATH_MAX]; char newsize_str[16] = { 0 }; const char *argv[FS_CMD_MAX_ARGS + 4]; char *devpath; int args = 0; int status; if (dm_snprintf(newsize_str, sizeof(newsize_str), "%llu", (unsigned long long)fsi->new_size_bytes) < 0) return_0; if (dm_snprintf(lv_path, PATH_MAX, "%s%s/%s", lv->vg->cmd->dev_dir, lv->vg->name, lv->name) < 0) return_0; argv[0] = _get_lvresize_fs_helper_path(cmd); argv[++args] = "--fsreduce"; argv[++args] = "--fstype"; argv[++args] = fsi->fstype; argv[++args] = "--lvpath"; argv[++args] = lv_path; if (fsi->new_size_bytes) { argv[++args] = "--newsizebytes"; argv[++args] = newsize_str; } if (fsi->mounted) { argv[++args] = "--mountdir"; argv[++args] = fsi->mount_dir; } if (fsi->needs_unmount) argv[++args] = "--unmount"; if (fsi->needs_mount) argv[++args] = "--mount"; if (fsi->needs_fsck) argv[++args] = "--fsck"; if (fsi->needs_crypt) { if (dm_snprintf(crypt_path, sizeof(crypt_path), "/dev/dm-%u", MINOR(fsi->crypt_devt)) < 0) return_0; argv[++args] = "--cryptresize"; argv[++args] = "--cryptpath"; argv[++args] = crypt_path; } /* * fsmode manage means the fs should be remounted after * resizing if it was unmounted. */ if (fsi->needs_unmount && !strcmp(fsmode, "manage")) argv[++args] = "--remount"; argv[++args] = NULL; devpath = fsi->needs_crypt ? crypt_path : (char *)display_lvname(lv); log_print_unless_silent("Reducing file system %s to %s (%llu bytes) on %s...", fsi->fstype, display_size(cmd, fsi->new_size_bytes/512), (unsigned long long)fsi->new_size_bytes, devpath); if (!exec_cmd(cmd, argv, &status, 1)) { log_error("Failed to reduce file system with lvresize_fs_helper."); return 0; } log_print_unless_silent("Reduced file system %s on %s.", fsi->fstype, devpath); return 1; } /* * The helper script does the following steps for extend: * devpath = $cryptpath ? $cryptpath : $lvpath * if needs_unmount * umount $mountdir * if needs_fsck * e2fsck -f -p $devpath * if needs_crypt * cryptsetup resize $cryptpath * if needs_mount * mount $devpath $tmpdir * if $fstype == "ext" * resize2fs $devpath * if $fstype == "xfs" * xfs_growfs $devpath * * Note: when a crypt layer is included, new_size_bytes is smaller * than newsize_bytes_lv because of the crypt header. */ int fs_extend_script(struct cmd_context *cmd, struct logical_volume *lv, struct fs_info *fsi, char *fsmode) { char lv_path[PATH_MAX]; char crypt_path[PATH_MAX]; char newsize_str[16] = { 0 }; const char *argv[FS_CMD_MAX_ARGS + 4]; char *devpath; int args = 0; int status; if (dm_snprintf(newsize_str, sizeof(newsize_str), "%llu", (unsigned long long)fsi->new_size_bytes) < 0) return_0; if (dm_snprintf(lv_path, PATH_MAX, "%s%s/%s", lv->vg->cmd->dev_dir, lv->vg->name, lv->name) < 0) return_0; argv[0] = _get_lvresize_fs_helper_path(cmd); argv[++args] = "--fsextend"; argv[++args] = "--fstype"; argv[++args] = fsi->fstype; argv[++args] = "--lvpath"; argv[++args] = lv_path; if (fsi->new_size_bytes) { argv[++args] = "--newsizebytes"; argv[++args] = newsize_str; } if (fsi->mounted) { argv[++args] = "--mountdir"; argv[++args] = fsi->mount_dir; } if (fsi->needs_unmount) argv[++args] = "--unmount"; if (fsi->needs_mount) argv[++args] = "--mount"; if (fsi->needs_fsck) argv[++args] = "--fsck"; if (fsi->needs_crypt) { if (dm_snprintf(crypt_path, sizeof(crypt_path), "/dev/dm-%u", MINOR(fsi->crypt_devt)) < 0) return_0; argv[++args] = "--cryptresize"; argv[++args] = "--cryptpath"; argv[++args] = crypt_path; } /* * fsmode manage means the fs should be remounted after * resizing if it was unmounted. */ if (fsi->needs_unmount && !strcmp(fsmode, "manage")) argv[++args] = "--remount"; argv[++args] = NULL; devpath = fsi->needs_crypt ? crypt_path : (char *)display_lvname(lv); log_print_unless_silent("Extending file system %s to %s (%llu bytes) on %s...", fsi->fstype, display_size(cmd, fsi->new_size_bytes/512), (unsigned long long)fsi->new_size_bytes, devpath); if (!exec_cmd(cmd, argv, &status, 1)) { log_error("Failed to extend file system with lvresize_fs_helper."); return 0; } log_print_unless_silent("Extended file system %s on %s.", fsi->fstype, devpath); return 1; }