1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <linux/btrfs.h>
4 #include <linux/magic.h>
5 #include <sys/ioctl.h>
6 #include <sys/vfs.h>
7 
8 #include "blockdev-util.h"
9 #include "fs-util.h"
10 #include "missing_fs.h"
11 #include "missing_magic.h"
12 #include "missing_xfs.h"
13 #include "resize-fs.h"
14 #include "stat-util.h"
15 
resize_fs(int fd,uint64_t sz,uint64_t * ret_size)16 int resize_fs(int fd, uint64_t sz, uint64_t *ret_size) {
17         struct statfs sfs;
18 
19         assert(fd >= 0);
20 
21         /* Rounds down to next block size */
22 
23         if (sz <= 0 || sz == UINT64_MAX)
24                 return -ERANGE;
25 
26         if (fstatfs(fd, &sfs) < 0)
27                 return -errno;
28 
29         if (is_fs_type(&sfs, EXT4_SUPER_MAGIC)) {
30                 uint64_t u;
31 
32                 if (sz < EXT4_MINIMAL_SIZE)
33                         return -ERANGE;
34 
35                 u = sz / sfs.f_bsize;
36 
37                 if (ioctl(fd, EXT4_IOC_RESIZE_FS, &u) < 0)
38                         return -errno;
39 
40                 if (ret_size)
41                         *ret_size = u * sfs.f_bsize;
42 
43         } else if (is_fs_type(&sfs, BTRFS_SUPER_MAGIC)) {
44                 struct btrfs_ioctl_vol_args args = {};
45 
46                 /* 256M is the minimize size enforced by the btrfs kernel code when resizing (which is
47                  * strange btw, as mkfs.btrfs is fine creating file systems > 109M). It will return EINVAL in
48                  * that case, let's catch this error beforehand though, and report a more explanatory
49                  * error. */
50 
51                 if (sz < BTRFS_MINIMAL_SIZE)
52                         return -ERANGE;
53 
54                 sz -= sz % sfs.f_bsize;
55 
56                 xsprintf(args.name, "%" PRIu64, sz);
57 
58                 if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
59                         return -errno;
60 
61                 if (ret_size)
62                         *ret_size = sz;
63 
64         } else if (is_fs_type(&sfs, XFS_SB_MAGIC)) {
65                 xfs_fsop_geom_t geo;
66                 xfs_growfs_data_t d;
67 
68                 if (sz < XFS_MINIMAL_SIZE)
69                         return -ERANGE;
70 
71                 if (ioctl(fd, XFS_IOC_FSGEOMETRY, &geo) < 0)
72                         return -errno;
73 
74                 d = (xfs_growfs_data_t) {
75                         .imaxpct = geo.imaxpct,
76                         .newblocks = sz / geo.blocksize,
77                 };
78 
79                 if (ioctl(fd, XFS_IOC_FSGROWFSDATA, &d) < 0)
80                         return -errno;
81 
82                 if (ret_size)
83                         *ret_size = d.newblocks * geo.blocksize;
84 
85         } else
86                 return -EOPNOTSUPP;
87 
88         return 0;
89 }
90 
minimal_size_by_fs_magic(statfs_f_type_t magic)91 uint64_t minimal_size_by_fs_magic(statfs_f_type_t magic) {
92 
93         switch (magic) {
94 
95         case (statfs_f_type_t) EXT4_SUPER_MAGIC:
96                 return EXT4_MINIMAL_SIZE;
97 
98         case (statfs_f_type_t) XFS_SB_MAGIC:
99                 return XFS_MINIMAL_SIZE;
100 
101         case (statfs_f_type_t) BTRFS_SUPER_MAGIC:
102                 return  BTRFS_MINIMAL_SIZE;
103 
104         default:
105                 return UINT64_MAX;
106         }
107 }
108 
minimal_size_by_fs_name(const char * name)109 uint64_t minimal_size_by_fs_name(const char *name) {
110 
111         if (streq_ptr(name, "ext4"))
112                 return EXT4_MINIMAL_SIZE;
113 
114         if (streq_ptr(name, "xfs"))
115                 return XFS_MINIMAL_SIZE;
116 
117         if (streq_ptr(name, "btrfs"))
118                 return BTRFS_MINIMAL_SIZE;
119 
120         return UINT64_MAX;
121 }
122 
123 /* Returns true for the only fs that can online shrink *and* grow */
fs_can_online_shrink_and_grow(statfs_f_type_t magic)124 bool fs_can_online_shrink_and_grow(statfs_f_type_t magic) {
125         return magic == (statfs_f_type_t) BTRFS_SUPER_MAGIC;
126 }
127