Merge branch 'for-chris' of git://github.com/idryomov/btrfs-progs into 0.20
[platform/upstream/btrfs-progs.git] / cmds-filesystem.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public
4  * License v2 as published by the Free Software Foundation.
5  *
6  * This program is distributed in the hope that it will be useful,
7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
9  * General Public License for more details.
10  *
11  * You should have received a copy of the GNU General Public
12  * License along with this program; if not, write to the
13  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
14  * Boston, MA 021110-1307, USA.
15  */
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 #include <sys/ioctl.h>
22 #include <errno.h>
23 #include <uuid/uuid.h>
24 #include <ctype.h>
25
26 #include "kerncompat.h"
27 #include "ctree.h"
28 #include "ioctl.h"
29 #include "utils.h"
30 #include "volumes.h"
31
32 #include "version.h"
33
34 #include "commands.h"
35 #include "btrfslabel.h"
36
37 static const char * const filesystem_cmd_group_usage[] = {
38         "btrfs filesystem [<group>] <command> [<args>]",
39         NULL
40 };
41
42 static const char * const cmd_df_usage[] = {
43         "btrfs filesystem df <path>",
44         "Show space usage information for a mount point",
45         NULL
46 };
47
48 static int cmd_df(int argc, char **argv)
49 {
50         struct btrfs_ioctl_space_args *sargs;
51         u64 count = 0, i;
52         int ret;
53         int fd;
54         int e;
55         char *path;
56
57         if (check_argc_exact(argc, 2))
58                 usage(cmd_df_usage);
59
60         path = argv[1];
61
62         fd = open_file_or_dir(path);
63         if (fd < 0) {
64                 fprintf(stderr, "ERROR: can't access to '%s'\n", path);
65                 return 12;
66         }
67
68         sargs = malloc(sizeof(struct btrfs_ioctl_space_args));
69         if (!sargs)
70                 return -ENOMEM;
71
72         sargs->space_slots = 0;
73         sargs->total_spaces = 0;
74
75         ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs);
76         e = errno;
77         if (ret) {
78                 fprintf(stderr, "ERROR: couldn't get space info on '%s' - %s\n",
79                         path, strerror(e));
80                 free(sargs);
81                 return ret;
82         }
83         if (!sargs->total_spaces)
84                 return 0;
85
86         count = sargs->total_spaces;
87
88         sargs = realloc(sargs, sizeof(struct btrfs_ioctl_space_args) +
89                         (count * sizeof(struct btrfs_ioctl_space_info)));
90         if (!sargs)
91                 return -ENOMEM;
92
93         sargs->space_slots = count;
94         sargs->total_spaces = 0;
95
96         ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs);
97         e = errno;
98         if (ret) {
99                 fprintf(stderr, "ERROR: couldn't get space info on '%s' - %s\n",
100                         path, strerror(e));
101                 close(fd);
102                 free(sargs);
103                 return ret;
104         }
105
106         for (i = 0; i < sargs->total_spaces; i++) {
107                 char description[80];
108                 char *total_bytes;
109                 char *used_bytes;
110                 int written = 0;
111                 u64 flags = sargs->spaces[i].flags;
112
113                 memset(description, 0, 80);
114
115                 if (flags & BTRFS_BLOCK_GROUP_DATA) {
116                         if (flags & BTRFS_BLOCK_GROUP_METADATA) {
117                                 snprintf(description, 14, "%s",
118                                          "Data+Metadata");
119                                 written += 13;
120                         } else {
121                                 snprintf(description, 5, "%s", "Data");
122                                 written += 4;
123                         }
124                 } else if (flags & BTRFS_BLOCK_GROUP_SYSTEM) {
125                         snprintf(description, 7, "%s", "System");
126                         written += 6;
127                 } else if (flags & BTRFS_BLOCK_GROUP_METADATA) {
128                         snprintf(description, 9, "%s", "Metadata");
129                         written += 8;
130                 }
131
132                 if (flags & BTRFS_BLOCK_GROUP_RAID0) {
133                         snprintf(description+written, 8, "%s", ", RAID0");
134                         written += 7;
135                 } else if (flags & BTRFS_BLOCK_GROUP_RAID1) {
136                         snprintf(description+written, 8, "%s", ", RAID1");
137                         written += 7;
138                 } else if (flags & BTRFS_BLOCK_GROUP_DUP) {
139                         snprintf(description+written, 6, "%s", ", DUP");
140                         written += 5;
141                 } else if (flags & BTRFS_BLOCK_GROUP_RAID10) {
142                         snprintf(description+written, 9, "%s", ", RAID10");
143                         written += 8;
144                 }
145
146                 total_bytes = pretty_sizes(sargs->spaces[i].total_bytes);
147                 used_bytes = pretty_sizes(sargs->spaces[i].used_bytes);
148                 printf("%s: total=%s, used=%s\n", description, total_bytes,
149                        used_bytes);
150         }
151         free(sargs);
152
153         return 0;
154 }
155
156 static int uuid_search(struct btrfs_fs_devices *fs_devices, char *search)
157 {
158         char uuidbuf[37];
159         struct list_head *cur;
160         struct btrfs_device *device;
161         int search_len = strlen(search);
162
163         search_len = min(search_len, 37);
164         uuid_unparse(fs_devices->fsid, uuidbuf);
165         if (!strncmp(uuidbuf, search, search_len))
166                 return 1;
167
168         list_for_each(cur, &fs_devices->devices) {
169                 device = list_entry(cur, struct btrfs_device, dev_list);
170                 if ((device->label && strcmp(device->label, search) == 0) ||
171                     strcmp(device->name, search) == 0)
172                         return 1;
173         }
174         return 0;
175 }
176
177 static void print_one_uuid(struct btrfs_fs_devices *fs_devices)
178 {
179         char uuidbuf[37];
180         struct list_head *cur;
181         struct btrfs_device *device;
182         char *super_bytes_used;
183         u64 devs_found = 0;
184         u64 total;
185
186         uuid_unparse(fs_devices->fsid, uuidbuf);
187         device = list_entry(fs_devices->devices.next, struct btrfs_device,
188                             dev_list);
189         if (device->label && device->label[0])
190                 printf("Label: '%s' ", device->label);
191         else
192                 printf("Label: none ");
193
194         super_bytes_used = pretty_sizes(device->super_bytes_used);
195
196         total = device->total_devs;
197         printf(" uuid: %s\n\tTotal devices %llu FS bytes used %s\n", uuidbuf,
198                (unsigned long long)total, super_bytes_used);
199
200         free(super_bytes_used);
201
202         list_for_each(cur, &fs_devices->devices) {
203                 char *total_bytes;
204                 char *bytes_used;
205                 device = list_entry(cur, struct btrfs_device, dev_list);
206                 total_bytes = pretty_sizes(device->total_bytes);
207                 bytes_used = pretty_sizes(device->bytes_used);
208                 printf("\tdevid %4llu size %s used %s path %s\n",
209                        (unsigned long long)device->devid,
210                        total_bytes, bytes_used, device->name);
211                 free(total_bytes);
212                 free(bytes_used);
213                 devs_found++;
214         }
215         if (devs_found < total) {
216                 printf("\t*** Some devices missing\n");
217         }
218         printf("\n");
219 }
220
221 static const char * const cmd_show_usage[] = {
222         "btrfs filesystem show [--all-devices] [<uuid>|<label>]",
223         "Show the structure of a filesystem",
224         "If no argument is given, structure of all present filesystems is shown.",
225         NULL
226 };
227
228 static int cmd_show(int argc, char **argv)
229 {
230         struct list_head *all_uuids;
231         struct btrfs_fs_devices *fs_devices;
232         struct list_head *cur_uuid;
233         char *search = 0;
234         int ret;
235         int checklist = 1;
236         int searchstart = 1;
237
238         if( argc > 1 && !strcmp(argv[1],"--all-devices")){
239                 checklist = 0;
240                 searchstart += 1;
241         }
242
243         if (check_argc_max(argc, searchstart + 1))
244                 usage(cmd_show_usage);
245
246         if(checklist)
247                 ret = btrfs_scan_block_devices(0);
248         else
249                 ret = btrfs_scan_one_dir("/dev", 0);
250
251         if (ret){
252                 fprintf(stderr, "ERROR: error %d while scanning\n", ret);
253                 return 18;
254         }
255         
256         if(searchstart < argc)
257                 search = argv[searchstart];
258
259         all_uuids = btrfs_scanned_uuids();
260         list_for_each(cur_uuid, all_uuids) {
261                 fs_devices = list_entry(cur_uuid, struct btrfs_fs_devices,
262                                         list);
263                 if (search && uuid_search(fs_devices, search) == 0)
264                         continue;
265                 print_one_uuid(fs_devices);
266         }
267         printf("%s\n", BTRFS_BUILD_VERSION);
268         return 0;
269 }
270
271 static const char * const cmd_sync_usage[] = {
272         "btrfs filesystem sync <path>",
273         "Force a sync on a filesystem",
274         NULL
275 };
276
277 static int cmd_sync(int argc, char **argv)
278 {
279         int     fd, res, e;
280         char    *path;
281
282         if (check_argc_exact(argc, 2))
283                 usage(cmd_sync_usage);
284
285         path = argv[1];
286
287         fd = open_file_or_dir(path);
288         if (fd < 0) {
289                 fprintf(stderr, "ERROR: can't access to '%s'\n", path);
290                 return 12;
291         }
292
293         printf("FSSync '%s'\n", path);
294         res = ioctl(fd, BTRFS_IOC_SYNC);
295         e = errno;
296         close(fd);
297         if( res < 0 ){
298                 fprintf(stderr, "ERROR: unable to fs-syncing '%s' - %s\n", 
299                         path, strerror(e));
300                 return 16;
301         }
302
303         return 0;
304 }
305
306 static u64 parse_size(char *s)
307 {
308         int len = strlen(s);
309         char c;
310         u64 mult = 1;
311
312         if (!isdigit(s[len - 1])) {
313                 c = tolower(s[len - 1]);
314                 switch (c) {
315                 case 'g':
316                         mult *= 1024;
317                 case 'm':
318                         mult *= 1024;
319                 case 'k':
320                         mult *= 1024;
321                 case 'b':
322                         break;
323                 default:
324                         fprintf(stderr, "Unknown size descriptor %c\n", c);
325                         exit(1);
326                 }
327                 s[len - 1] = '\0';
328         }
329         return atoll(s) * mult;
330 }
331
332 static int parse_compress_type(char *s)
333 {
334         if (strcmp(optarg, "zlib") == 0)
335                 return BTRFS_COMPRESS_ZLIB;
336         else if (strcmp(optarg, "lzo") == 0)
337                 return BTRFS_COMPRESS_LZO;
338         else {
339                 fprintf(stderr, "Unknown compress type %s\n", s);
340                 exit(1);
341         };
342 }
343
344 static const char * const cmd_defrag_usage[] = {
345         "btrfs filesystem defragment [options] <file>|<dir> [<file>|<dir>...]",
346         "Defragment a file or a directory",
347         "",
348         "-v             be verbose",
349         "-c[zlib,lzo]   compress the file while defragmenting",
350         "-f             flush data to disk immediately after defragmenting",
351         "-s start       defragment only from byte onward",
352         "-l len         defragment only up to len bytes",
353         "-t size        minimal size of file to be considered for defragmenting",
354         NULL
355 };
356
357 static int cmd_defrag(int argc, char **argv)
358 {
359         int fd;
360         int flush = 0;
361         u64 start = 0;
362         u64 len = (u64)-1;
363         u32 thresh = 0;
364         int i;
365         int errors = 0;
366         int ret = 0;
367         int verbose = 0;
368         int fancy_ioctl = 0;
369         struct btrfs_ioctl_defrag_range_args range;
370         int e=0;
371         int compress_type = BTRFS_COMPRESS_NONE;
372
373         optind = 1;
374         while(1) {
375                 int c = getopt(argc, argv, "vc::fs:l:t:");
376                 if (c < 0)
377                         break;
378
379                 switch(c) {
380                 case 'c':
381                         compress_type = BTRFS_COMPRESS_ZLIB;
382                         if (optarg)
383                                 compress_type = parse_compress_type(optarg);
384                         fancy_ioctl = 1;
385                         break;
386                 case 'f':
387                         flush = 1;
388                         fancy_ioctl = 1;
389                         break;
390                 case 'v':
391                         verbose = 1;
392                         break;
393                 case 's':
394                         start = parse_size(optarg);
395                         fancy_ioctl = 1;
396                         break;
397                 case 'l':
398                         len = parse_size(optarg);
399                         fancy_ioctl = 1;
400                         break;
401                 case 't':
402                         thresh = parse_size(optarg);
403                         fancy_ioctl = 1;
404                         break;
405                 default:
406                         usage(cmd_defrag_usage);
407                 }
408         }
409
410         if (check_argc_min(argc - optind, 1))
411                 usage(cmd_defrag_usage);
412
413         memset(&range, 0, sizeof(range));
414         range.start = start;
415         range.len = len;
416         range.extent_thresh = thresh;
417         if (compress_type) {
418                 range.flags |= BTRFS_DEFRAG_RANGE_COMPRESS;
419                 range.compress_type = compress_type;
420         }
421         if (flush)
422                 range.flags |= BTRFS_DEFRAG_RANGE_START_IO;
423
424         for (i = optind; i < argc; i++) {
425                 if (verbose)
426                         printf("%s\n", argv[i]);
427                 fd = open_file_or_dir(argv[i]);
428                 if (fd < 0) {
429                         fprintf(stderr, "failed to open %s\n", argv[i]);
430                         perror("open:");
431                         errors++;
432                         continue;
433                 }
434                 if (!fancy_ioctl) {
435                         ret = ioctl(fd, BTRFS_IOC_DEFRAG, NULL);
436                         e=errno;
437                 } else {
438                         ret = ioctl(fd, BTRFS_IOC_DEFRAG_RANGE, &range);
439                         if (ret && errno == ENOTTY) {
440                                 fprintf(stderr, "ERROR: defrag range ioctl not "
441                                         "supported in this kernel, please try "
442                                         "without any options.\n");
443                                 errors++;
444                                 close(fd);
445                                 break;
446                         }
447                 }
448                 if (ret) {
449                         fprintf(stderr, "ERROR: defrag failed on %s - %s\n",
450                                 argv[i], strerror(e));
451                         errors++;
452                 }
453                 close(fd);
454         }
455         if (verbose)
456                 printf("%s\n", BTRFS_BUILD_VERSION);
457         if (errors) {
458                 fprintf(stderr, "total %d failures\n", errors);
459                 exit(1);
460         }
461
462         return errors + 20;
463 }
464
465 static const char * const cmd_resize_usage[] = {
466         "btrfs filesystem resize [devid:][+/-]<newsize>[gkm]|[devid:]max <path>",
467         "Resize a filesystem",
468         "If 'max' is passed, the filesystem will occupy all available space",
469         "on the device 'devid'.",
470         NULL
471 };
472
473 static int cmd_resize(int argc, char **argv)
474 {
475         struct btrfs_ioctl_vol_args     args;
476         int     fd, res, len, e;
477         char    *amount, *path;
478
479         if (check_argc_exact(argc, 3))
480                 usage(cmd_resize_usage);
481
482         amount = argv[1];
483         path = argv[2];
484
485         fd = open_file_or_dir(path);
486         if (fd < 0) {
487                 fprintf(stderr, "ERROR: can't access to '%s'\n", path);
488                 return 12;
489         }
490         len = strlen(amount);
491         if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
492                 fprintf(stderr, "ERROR: size value too long ('%s)\n",
493                         amount);
494                 return 14;
495         }
496
497         printf("Resize '%s' of '%s'\n", path, amount);
498         strncpy(args.name, amount, BTRFS_PATH_NAME_MAX);
499         args.name[BTRFS_PATH_NAME_MAX-1] = 0;
500         res = ioctl(fd, BTRFS_IOC_RESIZE, &args);
501         e = errno;
502         close(fd);
503         if( res < 0 ){
504                 fprintf(stderr, "ERROR: unable to resize '%s' - %s\n", 
505                         path, strerror(e));
506                 return 30;
507         }
508         return 0;
509 }
510
511 static const char * const cmd_label_usage[] = {
512         "btrfs filesystem label <device> [<newlabel>]",
513         "Get or change the label of an unmounted filesystem",
514         "With one argument, get the label of filesystem on <device>.",
515         "If <newlabel> is passed, set the filesystem label to <newlabel>.",
516         NULL
517 };
518
519 static int cmd_label(int argc, char **argv)
520 {
521         if (check_argc_min(argc, 2) || check_argc_max(argc, 3))
522                 usage(cmd_label_usage);
523
524         if (argc > 2)
525                 return set_label(argv[1], argv[2]);
526         else
527                 return get_label(argv[1]);
528 }
529
530 const struct cmd_group filesystem_cmd_group = {
531         filesystem_cmd_group_usage, NULL, {
532                 { "df", cmd_df, cmd_df_usage, NULL, 0 },
533                 { "show", cmd_show, cmd_show_usage, NULL, 0 },
534                 { "sync", cmd_sync, cmd_sync_usage, NULL, 0 },
535                 { "defragment", cmd_defrag, cmd_defrag_usage, NULL, 0 },
536                 { "balance", cmd_balance, NULL, &balance_cmd_group, 1 },
537                 { "resize", cmd_resize, cmd_resize_usage, NULL, 0 },
538                 { "label", cmd_label, cmd_label_usage, NULL, 0 },
539                 { 0, 0, 0, 0, 0 },
540         }
541 };
542
543 int cmd_filesystem(int argc, char **argv)
544 {
545         return handle_command_group(&filesystem_cmd_group, argc, argv);
546 }