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