| 1 | /* |
| 2 | * Copyright (c) 2009 The DragonFly Project. All rights reserved. |
| 3 | * |
| 4 | * This code is derived from software contributed to The DragonFly Project |
| 5 | * by Antonio Huete <tuxillo@quantumachine.net> |
| 6 | * |
| 7 | * Redistribution and use in source and binary forms, with or without |
| 8 | * modification, are permitted provided that the following conditions |
| 9 | * are met: |
| 10 | * |
| 11 | * 1. Redistributions of source code must retain the above copyright |
| 12 | * notice, this list of conditions and the following disclaimer. |
| 13 | * 2. Redistributions in binary form must reproduce the above copyright |
| 14 | * notice, this list of conditions and the following disclaimer in |
| 15 | * the documentation and/or other materials provided with the |
| 16 | * distribution. |
| 17 | * 3. Neither the name of The DragonFly Project nor the names of its |
| 18 | * contributors may be used to endorse or promote products derived |
| 19 | * from this software without specific, prior written permission. |
| 20 | * |
| 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 22 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
| 24 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
| 25 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| 26 | * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| 27 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| 28 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
| 29 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| 30 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| 31 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 32 | * SUCH DAMAGE. |
| 33 | * |
| 34 | */ |
| 35 | #include "hammer.h" |
| 36 | #include <libutil.h> |
| 37 | |
| 38 | void show_info(char *path); |
| 39 | char *find_pfs_mount(int pfsid, uuid_t parentuuid, int ismaster); |
| 40 | double percent(int64_t value, int64_t total); |
| 41 | u_int32_t count_snapshots(int fd, u_int32_t version, char *pfs_snapshots, char *mountedon); |
| 42 | |
| 43 | void |
| 44 | hammer_cmd_info(void) |
| 45 | { |
| 46 | struct statfs *stfsbuf; |
| 47 | int mntsize, i, first = 1; |
| 48 | char *fstype, *path; |
| 49 | |
| 50 | tzset(); |
| 51 | mntsize = getmntinfo(&stfsbuf, MNT_NOWAIT); |
| 52 | if (mntsize > 0) { |
| 53 | for (i=0; i < mntsize; i++) { |
| 54 | fstype = stfsbuf[i].f_fstypename; |
| 55 | path = stfsbuf[i].f_mntonname; |
| 56 | if ((strcmp(fstype, "hammer")) == 0) { |
| 57 | if (first) |
| 58 | first = 0; |
| 59 | else |
| 60 | fprintf(stdout, "\n"); |
| 61 | show_info(path); |
| 62 | } |
| 63 | } |
| 64 | } else { |
| 65 | fprintf(stdout, "No mounted filesystems found\n"); |
| 66 | } |
| 67 | |
| 68 | } |
| 69 | |
| 70 | void |
| 71 | show_info(char *path) |
| 72 | { |
| 73 | struct hammer_pseudofs_data pfs_od; |
| 74 | struct hammer_ioc_pseudofs_rw pfs; |
| 75 | int64_t usedbigblocks, bytes; |
| 76 | struct hammer_ioc_info info; |
| 77 | int fd, pfs_id, ismaster; |
| 78 | char *fsid; |
| 79 | char *mountedon; |
| 80 | char buf[6]; |
| 81 | u_int32_t sc; |
| 82 | |
| 83 | fsid = mountedon = NULL; |
| 84 | usedbigblocks = 0; |
| 85 | pfs_id = 0; /* Include PFS#0 */ |
| 86 | bytes = 0; |
| 87 | sc = 0; |
| 88 | |
| 89 | bzero(&info, sizeof(struct hammer_ioc_info)); |
| 90 | |
| 91 | /* Try to get a file descriptor based on the path given */ |
| 92 | fd = open(path, O_RDONLY); |
| 93 | if (fd < 0) { |
| 94 | perror("show_info"); |
| 95 | exit(EXIT_FAILURE); |
| 96 | } |
| 97 | |
| 98 | if ((ioctl(fd, HAMMERIOC_GET_INFO, &info)) < 0) { |
| 99 | perror("show_info"); |
| 100 | exit(EXIT_FAILURE); |
| 101 | } |
| 102 | |
| 103 | /* Find out the UUID strings */ |
| 104 | uuid_to_string(&info.vol_fsid, &fsid, NULL); |
| 105 | |
| 106 | /* Volume information */ |
| 107 | fprintf(stdout, "Volume identification\n"); |
| 108 | fprintf(stdout, "\tLabel %s\n", info.vol_name); |
| 109 | fprintf(stdout, "\tNo. Volumes %d\n", info.nvolumes); |
| 110 | fprintf(stdout, "\tFSID %s\n", fsid); |
| 111 | fprintf(stdout, "\tHAMMER Version %d\n", info.version); |
| 112 | |
| 113 | /* Big blocks information */ |
| 114 | usedbigblocks = info.bigblocks - info.freebigblocks; |
| 115 | |
| 116 | fprintf(stdout, "Big block information\n"); |
| 117 | fprintf(stdout, "\tTotal %10jd\n", (intmax_t)info.bigblocks); |
| 118 | fprintf(stdout, "\tUsed %10jd (%.2lf%%)\n" |
| 119 | "\tReserved %10jd (%.2lf%%)\n" |
| 120 | "\tFree %10jd (%.2lf%%)\n", |
| 121 | (intmax_t)usedbigblocks, |
| 122 | percent(usedbigblocks, info.bigblocks), |
| 123 | (intmax_t)info.rsvbigblocks, |
| 124 | percent(info.rsvbigblocks, info.bigblocks), |
| 125 | (intmax_t)(info.freebigblocks - info.rsvbigblocks), |
| 126 | percent(info.freebigblocks - info.rsvbigblocks, |
| 127 | info.bigblocks)); |
| 128 | fprintf(stdout, "Space information\n"); |
| 129 | |
| 130 | /* Space information */ |
| 131 | fprintf(stdout, "\tNo. Inodes %10jd\n", (intmax_t)info.inodes); |
| 132 | bytes = (info.bigblocks << HAMMER_LARGEBLOCK_BITS); |
| 133 | humanize_number(buf, sizeof(buf) - (bytes < 0 ? 0 : 1), bytes, "", |
| 134 | HN_AUTOSCALE, HN_DECIMAL | HN_NOSPACE | HN_B); |
| 135 | fprintf(stdout, "\tTotal size %6s (%jd bytes)\n", |
| 136 | buf, (intmax_t)bytes); |
| 137 | |
| 138 | bytes = (usedbigblocks << HAMMER_LARGEBLOCK_BITS); |
| 139 | humanize_number(buf, sizeof(buf) - (bytes < 0 ? 0 : 1), bytes, "", |
| 140 | HN_AUTOSCALE, HN_DECIMAL | HN_NOSPACE | HN_B); |
| 141 | fprintf(stdout, "\tUsed space %6s\n", buf); |
| 142 | |
| 143 | bytes = (info.rsvbigblocks << HAMMER_LARGEBLOCK_BITS); |
| 144 | humanize_number(buf, sizeof(buf) - (bytes < 0 ? 0 : 1), bytes, "", |
| 145 | HN_AUTOSCALE, HN_DECIMAL | HN_NOSPACE | HN_B); |
| 146 | fprintf(stdout, "\tReserved space %6s\n", buf); |
| 147 | |
| 148 | bytes = ((info.freebigblocks - info.rsvbigblocks) << HAMMER_LARGEBLOCK_BITS); |
| 149 | humanize_number(buf, sizeof(buf) - (bytes < 0 ? 0 : 1), bytes, "", |
| 150 | HN_AUTOSCALE, HN_DECIMAL | HN_NOSPACE | HN_B); |
| 151 | fprintf(stdout, "\tFree space %6s\n", buf); |
| 152 | |
| 153 | /* Pseudo-filesystem information */ |
| 154 | fprintf(stdout, "PFS information\n"); |
| 155 | fprintf(stdout, "\tPFS-Id Mode Snaps Mounted-on\n"); |
| 156 | |
| 157 | while(pfs_id < HAMMER_MAX_PFS) { |
| 158 | bzero(&pfs, sizeof(pfs)); |
| 159 | bzero(&pfs_od, sizeof(pfs_od)); |
| 160 | pfs.pfs_id = pfs_id; |
| 161 | pfs.ondisk = &pfs_od; |
| 162 | pfs.bytes = sizeof(pfs_od); |
| 163 | pfs.version = HAMMER_IOC_PSEUDOFS_VERSION; |
| 164 | if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) >= 0) { |
| 165 | ismaster = (pfs_od.mirror_flags & HAMMER_PFSD_SLAVE) ? 0 : 1; |
| 166 | if (pfs_id == 0) |
| 167 | mountedon = path; |
| 168 | else |
| 169 | mountedon = find_pfs_mount(pfs_id, info.vol_fsid, ismaster); |
| 170 | |
| 171 | sc = count_snapshots(fd, info.version, pfs_od.snapshots, mountedon); |
| 172 | |
| 173 | fprintf(stdout, "\t%6d %-6s %6d ", |
| 174 | pfs_id, (ismaster ? "MASTER" : "SLAVE"), sc); |
| 175 | if (mountedon) |
| 176 | fprintf(stdout, "%s", mountedon); |
| 177 | else |
| 178 | fprintf(stdout, "not-mounted"); |
| 179 | fprintf(stdout, "\n"); |
| 180 | } |
| 181 | pfs_id++; |
| 182 | } |
| 183 | |
| 184 | free(fsid); |
| 185 | free(mountedon); |
| 186 | } |
| 187 | |
| 188 | char * |
| 189 | find_pfs_mount(int pfsid, uuid_t parentuuid, int ismaster) |
| 190 | { |
| 191 | struct hammer_ioc_info hi; |
| 192 | struct statfs *mntbuf; |
| 193 | int mntsize; |
| 194 | int curmount; |
| 195 | int fd; |
| 196 | size_t mntbufsize; |
| 197 | char *trailstr; |
| 198 | char *retval; |
| 199 | |
| 200 | retval = NULL; |
| 201 | |
| 202 | /* Do not continue if there are no mounted filesystems */ |
| 203 | mntsize = getfsstat(NULL, 0, MNT_NOWAIT); |
| 204 | if (mntsize <= 0) |
| 205 | return retval; |
| 206 | |
| 207 | mntbufsize = (mntsize) * sizeof(struct statfs); |
| 208 | mntbuf = malloc(mntbufsize); |
| 209 | if (mntbuf == NULL) { |
| 210 | perror("show_info"); |
| 211 | exit(EXIT_FAILURE); |
| 212 | } |
| 213 | |
| 214 | mntsize = getfsstat(mntbuf, (long)mntbufsize, MNT_NOWAIT); |
| 215 | curmount = mntsize - 1; |
| 216 | |
| 217 | asprintf(&trailstr, ":%05d", pfsid); |
| 218 | |
| 219 | /* |
| 220 | * Iterate all the mounted points looking for the PFS passed to |
| 221 | * this function. |
| 222 | */ |
| 223 | while(curmount >= 0) { |
| 224 | /* |
| 225 | * We need to avoid that PFS belonging to other HAMMER |
| 226 | * filesystems are showed as mounted, so we compare |
| 227 | * against the FSID, which is presumable to be unique. |
| 228 | */ |
| 229 | bzero(&hi, sizeof(hi)); |
| 230 | if ((fd = open(mntbuf[curmount].f_mntfromname, O_RDONLY)) < 0) { |
| 231 | curmount--; |
| 232 | continue; |
| 233 | } |
| 234 | |
| 235 | if ((ioctl(fd, HAMMERIOC_GET_INFO, &hi)) < 0) { |
| 236 | curmount--; |
| 237 | continue; |
| 238 | } |
| 239 | |
| 240 | if (strstr(mntbuf[curmount].f_mntfromname, trailstr) != NULL && |
| 241 | (uuid_compare(&hi.vol_fsid, &parentuuid, NULL)) == 0) { |
| 242 | if (ismaster) { |
| 243 | if (strstr(mntbuf[curmount].f_mntfromname, "@@-1") != NULL) { |
| 244 | retval = strdup(mntbuf[curmount].f_mntonname); |
| 245 | break; |
| 246 | } |
| 247 | } else { |
| 248 | if (strstr(mntbuf[curmount].f_mntfromname, "@@0x") != NULL ) { |
| 249 | retval = strdup(mntbuf[curmount].f_mntonname); |
| 250 | break; |
| 251 | } |
| 252 | } |
| 253 | } |
| 254 | curmount--; |
| 255 | } |
| 256 | free(trailstr); |
| 257 | return retval; |
| 258 | } |
| 259 | |
| 260 | double |
| 261 | percent(int64_t value, int64_t total) |
| 262 | { |
| 263 | /* Avoid divide-by-zero */ |
| 264 | if (total == 0) |
| 265 | return 100.0; |
| 266 | |
| 267 | return ((value * 100.0) / (double)total); |
| 268 | } |
| 269 | |
| 270 | u_int32_t |
| 271 | count_snapshots(int fd, u_int32_t version, char *pfs_snapshots, char *mountedon) |
| 272 | { |
| 273 | struct hammer_ioc_snapshot snapinfo; |
| 274 | char *snapshots_path, *fpath; |
| 275 | struct dirent *den; |
| 276 | struct stat st; |
| 277 | DIR *dir; |
| 278 | u_int32_t snapshot_count = 0; |
| 279 | |
| 280 | bzero(&snapinfo, sizeof(struct hammer_ioc_snapshot)); |
| 281 | if (version < 3) { |
| 282 | /* |
| 283 | * old style: count the number of softlinks in the snapshots dir |
| 284 | */ |
| 285 | if (pfs_snapshots[0]) |
| 286 | snapshots_path = pfs_snapshots; |
| 287 | else |
| 288 | asprintf(&snapshots_path, "%s/snapshots", mountedon); |
| 289 | if ((dir = opendir(snapshots_path)) != NULL) { |
| 290 | while ((den = readdir(dir)) != NULL) { |
| 291 | if (den->d_name[0] == '.') |
| 292 | continue; |
| 293 | asprintf(&fpath, "%s/%s", snapshots_path, den->d_name); |
| 294 | if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode)) |
| 295 | snapshot_count++; |
| 296 | free(fpath); |
| 297 | } |
| 298 | closedir(dir); |
| 299 | } |
| 300 | } else { |
| 301 | /* |
| 302 | * new style: file system meta-data |
| 303 | */ |
| 304 | do { |
| 305 | if (ioctl(fd, HAMMERIOC_GET_SNAPSHOT, &snapinfo) < 0) { |
| 306 | perror("count_snapshots"); |
| 307 | exit(EXIT_FAILURE); |
| 308 | } |
| 309 | snapshot_count += snapinfo.count; |
| 310 | } while (snapinfo.head.error == 0 && snapinfo.count); |
| 311 | } |
| 312 | return snapshot_count; |
| 313 | } |