btrfs-progs: subvol list: don't print deleted subvol as TOPLEVEL
[platform/upstream/btrfs-progs.git] / cmds-send.c
1 /*
2  * Copyright (C) 2012 Alexander Block.  All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public
6  * License v2 as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public
14  * License along with this program; if not, write to the
15  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16  * Boston, MA 021110-1307, USA.
17  */
18
19
20 #include "kerncompat.h"
21
22 #include <unistd.h>
23 #include <stdint.h>
24 #include <dirent.h>
25 #include <fcntl.h>
26 #include <pthread.h>
27 #include <math.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <sys/ioctl.h>
31 #include <libgen.h>
32 #include <mntent.h>
33 #include <assert.h>
34 #include <getopt.h>
35 #include <uuid/uuid.h>
36 #include <limits.h>
37
38 #include "ctree.h"
39 #include "ioctl.h"
40 #include "commands.h"
41 #include "list.h"
42 #include "utils.h"
43
44 #include "send.h"
45 #include "send-utils.h"
46 #include "help.h"
47
48 #define SEND_BUFFER_SIZE        SZ_64K
49
50 /*
51  * Default is 1 for historical reasons, changing may break scripts that expect
52  * the 'At subvol' message.
53  */
54 static int g_verbose = 1;
55
56 struct btrfs_send {
57         int send_fd;
58         int dump_fd;
59         int mnt_fd;
60
61         u64 *clone_sources;
62         u64 clone_sources_count;
63
64         char *root_path;
65         struct subvol_uuid_search sus;
66 };
67
68 static int get_root_id(struct btrfs_send *sctx, const char *path, u64 *root_id)
69 {
70         struct subvol_info *si;
71
72         si = subvol_uuid_search(&sctx->sus, 0, NULL, 0, path,
73                         subvol_search_by_path);
74         if (IS_ERR_OR_NULL(si)) {
75                 if (!si)
76                         return -ENOENT;
77                 else
78                         return PTR_ERR(si);
79         }
80         *root_id = si->root_id;
81         free(si->path);
82         free(si);
83         return 0;
84 }
85
86 static struct subvol_info *get_parent(struct btrfs_send *sctx, u64 root_id)
87 {
88         struct subvol_info *si_tmp;
89         struct subvol_info *si;
90
91         si_tmp = subvol_uuid_search(&sctx->sus, root_id, NULL, 0, NULL,
92                         subvol_search_by_root_id);
93         if (IS_ERR_OR_NULL(si_tmp))
94                 return si_tmp;
95
96         si = subvol_uuid_search(&sctx->sus, 0, si_tmp->parent_uuid, 0, NULL,
97                         subvol_search_by_uuid);
98         free(si_tmp->path);
99         free(si_tmp);
100         return si;
101 }
102
103 static int find_good_parent(struct btrfs_send *sctx, u64 root_id, u64 *found)
104 {
105         int ret;
106         struct subvol_info *parent = NULL;
107         struct subvol_info *parent2 = NULL;
108         struct subvol_info *best_parent = NULL;
109         u64 best_diff = (u64)-1;
110         int i;
111
112         parent = get_parent(sctx, root_id);
113         if (IS_ERR_OR_NULL(parent)) {
114                 if (!parent)
115                         ret = -ENOENT;
116                 else
117                         ret = PTR_ERR(parent);
118                 goto out;
119         }
120
121         for (i = 0; i < sctx->clone_sources_count; i++) {
122                 if (sctx->clone_sources[i] == parent->root_id) {
123                         best_parent = parent;
124                         parent = NULL;
125                         goto out_found;
126                 }
127         }
128
129         for (i = 0; i < sctx->clone_sources_count; i++) {
130                 s64 tmp;
131
132                 parent2 = get_parent(sctx, sctx->clone_sources[i]);
133                 if (IS_ERR_OR_NULL(parent2))
134                         continue;
135                 if (parent2->root_id != parent->root_id) {
136                         free(parent2->path);
137                         free(parent2);
138                         parent2 = NULL;
139                         continue;
140                 }
141
142                 free(parent2->path);
143                 free(parent2);
144                 parent2 = subvol_uuid_search(&sctx->sus,
145                                 sctx->clone_sources[i], NULL, 0, NULL,
146                                 subvol_search_by_root_id);
147                 if (IS_ERR_OR_NULL(parent2)) {
148                         if (!parent2)
149                                 ret = -ENOENT;
150                         else
151                                 ret = PTR_ERR(parent2);
152                         goto out;
153                 }
154                 tmp = parent2->ctransid - parent->ctransid;
155                 if (tmp < 0)
156                         tmp = -tmp;
157                 if (tmp < best_diff) {
158                         if (best_parent) {
159                                 free(best_parent->path);
160                                 free(best_parent);
161                         }
162                         best_parent = parent2;
163                         parent2 = NULL;
164                         best_diff = tmp;
165                 } else {
166                         free(parent2->path);
167                         free(parent2);
168                         parent2 = NULL;
169                 }
170         }
171
172         if (!best_parent) {
173                 ret = -ENOENT;
174                 goto out;
175         }
176
177 out_found:
178         *found = best_parent->root_id;
179         ret = 0;
180
181 out:
182         if (parent) {
183                 free(parent->path);
184                 free(parent);
185         }
186         if (best_parent) {
187                 free(best_parent->path);
188                 free(best_parent);
189         }
190         return ret;
191 }
192
193 static int add_clone_source(struct btrfs_send *sctx, u64 root_id)
194 {
195         void *tmp;
196
197         tmp = sctx->clone_sources;
198         sctx->clone_sources = realloc(sctx->clone_sources,
199                 sizeof(*sctx->clone_sources) * (sctx->clone_sources_count + 1));
200
201         if (!sctx->clone_sources) {
202                 free(tmp);
203                 return -ENOMEM;
204         }
205         sctx->clone_sources[sctx->clone_sources_count++] = root_id;
206
207         return 0;
208 }
209
210 #if 0
211 static int write_buf(int fd, const char *buf, size_t size)
212 {
213         int ret;
214         size_t pos = 0;
215
216         while (pos < size) {
217                 ssize_t wbytes;
218
219                 wbytes = write(fd, buf + pos, size - pos);
220                 if (wbytes < 0) {
221                         ret = -errno;
222                         error("failed to dump stream: %s", strerror(-ret));
223                         goto out;
224                 }
225                 if (!wbytes) {
226                         ret = -EIO;
227                         error("failed to dump stream: %s", strerror(-ret));
228                         goto out;
229                 }
230                 pos += wbytes;
231         }
232         ret = 0;
233
234 out:
235         return ret;
236 }
237
238 static void* read_sent_data_copy(void *arg)
239 {
240         int ret;
241         struct btrfs_send *sctx = (struct btrfs_send*)arg;
242         char buf[SEND_BUFFER_SIZE];
243
244         while (1) {
245                 ssize_t rbytes;
246
247                 rbytes = read(sctx->send_fd, buf, sizeof(buf));
248                 if (rbytes < 0) {
249                         ret = -errno;
250                         error("failed to read stream from kernel: %s",
251                                 strerror(-ret));
252                         goto out;
253                 }
254                 if (!rbytes) {
255                         ret = 0;
256                         goto out;
257                 }
258                 ret = write_buf(sctx->dump_fd, buf, rbytes);
259                 if (ret < 0)
260                         goto out;
261         }
262
263 out:
264         if (ret < 0)
265                 exit(-ret);
266
267         return ERR_PTR(ret);
268 }
269 #endif
270
271 static void *read_sent_data(void *arg)
272 {
273         int ret;
274         struct btrfs_send *sctx = (struct btrfs_send*)arg;
275
276         while (1) {
277                 ssize_t sbytes;
278
279                 /* Source is a pipe, output is either file or stdout */
280                 sbytes = splice(sctx->send_fd, NULL, sctx->dump_fd,
281                                 NULL, SEND_BUFFER_SIZE, SPLICE_F_MORE);
282                 if (sbytes < 0) {
283                         ret = -errno;
284                         error("failed to read stream from kernel: %s",
285                                 strerror(-ret));
286                         goto out;
287                 }
288                 if (!sbytes) {
289                         ret = 0;
290                         goto out;
291                 }
292         }
293
294 out:
295         if (ret < 0)
296                 exit(-ret);
297
298         return ERR_PTR(ret);
299 }
300
301 static int do_send(struct btrfs_send *send, u64 parent_root_id,
302                    int is_first_subvol, int is_last_subvol, const char *subvol,
303                    u64 flags)
304 {
305         int ret;
306         pthread_t t_read;
307         struct btrfs_ioctl_send_args io_send;
308         void *t_err = NULL;
309         int subvol_fd = -1;
310         int pipefd[2] = {-1, -1};
311
312         subvol_fd = openat(send->mnt_fd, subvol, O_RDONLY | O_NOATIME);
313         if (subvol_fd < 0) {
314                 ret = -errno;
315                 error("cannot open %s: %s", subvol, strerror(-ret));
316                 goto out;
317         }
318
319         ret = pipe(pipefd);
320         if (ret < 0) {
321                 ret = -errno;
322                 error("pipe failed: %s", strerror(-ret));
323                 goto out;
324         }
325
326         memset(&io_send, 0, sizeof(io_send));
327         io_send.send_fd = pipefd[1];
328         send->send_fd = pipefd[0];
329
330         if (!ret)
331                 ret = pthread_create(&t_read, NULL, read_sent_data, send);
332         if (ret) {
333                 ret = -ret;
334                 error("thread setup failed: %s", strerror(-ret));
335                 goto out;
336         }
337
338         io_send.flags = flags;
339         io_send.clone_sources = (__u64*)send->clone_sources;
340         io_send.clone_sources_count = send->clone_sources_count;
341         io_send.parent_root = parent_root_id;
342         if (!is_first_subvol)
343                 io_send.flags |= BTRFS_SEND_FLAG_OMIT_STREAM_HEADER;
344         if (!is_last_subvol)
345                 io_send.flags |= BTRFS_SEND_FLAG_OMIT_END_CMD;
346         ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
347         if (ret < 0) {
348                 ret = -errno;
349                 error("send ioctl failed with %d: %s", ret, strerror(-ret));
350                 if (ret == -EINVAL && (!is_first_subvol || !is_last_subvol))
351                         fprintf(stderr,
352                                 "Try upgrading your kernel or don't use -e.\n");
353                 goto out;
354         }
355         if (g_verbose > 1)
356                 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
357
358         if (g_verbose > 1)
359                 fprintf(stderr, "joining genl thread\n");
360
361         close(pipefd[1]);
362         pipefd[1] = -1;
363
364         ret = pthread_join(t_read, &t_err);
365         if (ret) {
366                 ret = -ret;
367                 error("pthread_join failed: %s", strerror(-ret));
368                 goto out;
369         }
370         if (t_err) {
371                 ret = (long int)t_err;
372                 error("failed to process send stream, ret=%ld (%s)",
373                                 (long int)t_err, strerror(-ret));
374                 goto out;
375         }
376
377         ret = 0;
378
379 out:
380         if (subvol_fd != -1)
381                 close(subvol_fd);
382         if (pipefd[0] != -1)
383                 close(pipefd[0]);
384         if (pipefd[1] != -1)
385                 close(pipefd[1]);
386         return ret;
387 }
388
389 static int init_root_path(struct btrfs_send *sctx, const char *subvol)
390 {
391         int ret = 0;
392
393         if (sctx->root_path)
394                 goto out;
395
396         ret = find_mount_root(subvol, &sctx->root_path);
397         if (ret < 0) {
398                 error("failed to determine mount point for %s: %s",
399                         subvol, strerror(-ret));
400                 ret = -EINVAL;
401                 goto out;
402         }
403         if (ret > 0) {
404                 error("%s doesn't belong to btrfs mount point", subvol);
405                 ret = -EINVAL;
406                 goto out;
407         }
408
409         sctx->mnt_fd = open(sctx->root_path, O_RDONLY | O_NOATIME);
410         if (sctx->mnt_fd < 0) {
411                 ret = -errno;
412                 error("cannot open '%s': %s", sctx->root_path, strerror(-ret));
413                 goto out;
414         }
415
416         ret = subvol_uuid_search_init(sctx->mnt_fd, &sctx->sus);
417         if (ret < 0) {
418                 error("failed to initialize subvol search: %s",
419                         strerror(-ret));
420                 goto out;
421         }
422
423 out:
424         return ret;
425
426 }
427
428 static int is_subvol_ro(struct btrfs_send *sctx, const char *subvol)
429 {
430         int ret;
431         u64 flags;
432         int fd = -1;
433
434         fd = openat(sctx->mnt_fd, subvol, O_RDONLY | O_NOATIME);
435         if (fd < 0) {
436                 ret = -errno;
437                 error("cannot open %s: %s", subvol, strerror(-ret));
438                 goto out;
439         }
440
441         ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
442         if (ret < 0) {
443                 ret = -errno;
444                 error("failed to get flags for subvolume %s: %s",
445                         subvol, strerror(-ret));
446                 goto out;
447         }
448
449         if (flags & BTRFS_SUBVOL_RDONLY)
450                 ret = 1;
451         else
452                 ret = 0;
453
454 out:
455         if (fd != -1)
456                 close(fd);
457
458         return ret;
459 }
460
461 static int set_root_info(struct btrfs_send *sctx, const char *subvol,
462                 u64 *root_id)
463 {
464         int ret;
465
466         ret = init_root_path(sctx, subvol);
467         if (ret < 0)
468                 goto out;
469
470         ret = get_root_id(sctx, subvol_strip_mountpoint(sctx->root_path, subvol),
471                 root_id);
472         if (ret < 0) {
473                 error("cannot resolve rootid for %s", subvol);
474                 goto out;
475         }
476
477 out:
478         return ret;
479 }
480
481 static void free_send_info(struct btrfs_send *sctx)
482 {
483         if (sctx->mnt_fd >= 0) {
484                 close(sctx->mnt_fd);
485                 sctx->mnt_fd = -1;
486         }
487         free(sctx->root_path);
488         sctx->root_path = NULL;
489         subvol_uuid_search_finit(&sctx->sus);
490 }
491
492 int cmd_send(int argc, char **argv)
493 {
494         char *subvol = NULL;
495         int ret;
496         char outname[PATH_MAX];
497         struct btrfs_send send;
498         u32 i;
499         char *mount_root = NULL;
500         char *snapshot_parent = NULL;
501         u64 root_id = 0;
502         u64 parent_root_id = 0;
503         int full_send = 1;
504         int new_end_cmd_semantic = 0;
505         u64 send_flags = 0;
506
507         memset(&send, 0, sizeof(send));
508         send.dump_fd = fileno(stdout);
509         outname[0] = 0;
510
511         while (1) {
512                 enum { GETOPT_VAL_SEND_NO_DATA = 256 };
513                 static const struct option long_options[] = {
514                         { "verbose", no_argument, NULL, 'v' },
515                         { "quiet", no_argument, NULL, 'q' },
516                         { "no-data", no_argument, NULL, GETOPT_VAL_SEND_NO_DATA }
517                 };
518                 int c = getopt_long(argc, argv, "vqec:f:i:p:", long_options, NULL);
519
520                 if (c < 0)
521                         break;
522
523                 switch (c) {
524                 case 'v':
525                         g_verbose++;
526                         break;
527                 case 'q':
528                         g_verbose = 0;
529                         break;
530                 case 'e':
531                         new_end_cmd_semantic = 1;
532                         break;
533                 case 'c':
534                         subvol = realpath(optarg, NULL);
535                         if (!subvol) {
536                                 ret = -errno;
537                                 error("realpath %s failed: %s\n", optarg, strerror(-ret));
538                                 goto out;
539                         }
540
541                         ret = set_root_info(&send, subvol, &root_id);
542                         if (ret < 0)
543                                 goto out;
544
545                         ret = is_subvol_ro(&send, subvol);
546                         if (ret < 0)
547                                 goto out;
548                         if (!ret) {
549                                 ret = -EINVAL;
550                                 error("cloned subvolume %s is not read-only", subvol);
551                                 goto out;
552                         }
553
554                         ret = add_clone_source(&send, root_id);
555                         if (ret < 0) {
556                                 error("cannot add clone source: %s", strerror(-ret));
557                                 goto out;
558                         }
559                         free(subvol);
560                         subvol = NULL;
561                         free_send_info(&send);
562                         full_send = 0;
563                         break;
564                 case 'f':
565                         if (arg_copy_path(outname, optarg, sizeof(outname))) {
566                                 error("output file path too long (%zu)", strlen(optarg));
567                                 ret = 1;
568                                 goto out;
569                         }
570                         break;
571                 case 'p':
572                         if (snapshot_parent) {
573                                 error("you cannot have more than one parent (-p)");
574                                 ret = 1;
575                                 goto out;
576                         }
577                         snapshot_parent = realpath(optarg, NULL);
578                         if (!snapshot_parent) {
579                                 ret = -errno;
580                                 error("realpath %s failed: %s", optarg, strerror(-ret));
581                                 goto out;
582                         }
583
584                         ret = is_subvol_ro(&send, snapshot_parent);
585                         if (ret < 0)
586                                 goto out;
587                         if (!ret) {
588                                 ret = -EINVAL;
589                                 error("parent subvolume %s is not read-only",
590                                         snapshot_parent);
591                                 goto out;
592                         }
593
594                         full_send = 0;
595                         break;
596                 case 'i':
597                         error("option -i was removed, use -c instead");
598                         ret = 1;
599                         goto out;
600                 case GETOPT_VAL_SEND_NO_DATA:
601                         send_flags |= BTRFS_SEND_FLAG_NO_FILE_DATA;
602                         break;
603                 case '?':
604                 default:
605                         error("send arguments invalid");
606                         ret = 1;
607                         goto out;
608                 }
609         }
610
611         if (check_argc_min(argc - optind, 1))
612                 usage(cmd_send_usage);
613
614         if (outname[0]) {
615                 int tmpfd;
616
617                 /*
618                  * Try to use an existing file first. Even if send runs as
619                  * root, it might not have permissions to create file (eg. on a
620                  * NFS) but it should still be able to use a pre-created file.
621                  */
622                 tmpfd = open(outname, O_WRONLY | O_TRUNC);
623                 if (tmpfd < 0) {
624                         if (errno == ENOENT)
625                                 tmpfd = open(outname,
626                                         O_CREAT | O_WRONLY | O_TRUNC, 0600);
627                 }
628                 send.dump_fd = tmpfd;
629                 if (send.dump_fd == -1) {
630                         ret = -errno;
631                         error("cannot create '%s': %s", outname, strerror(-ret));
632                         goto out;
633                 }
634         }
635
636         if (isatty(send.dump_fd)) {
637                 error(
638             "not dumping send stream into a terminal, redirect it into a file");
639                 ret = 1;
640                 goto out;
641         }
642
643         /* use first send subvol to determine mount_root */
644         subvol = realpath(argv[optind], NULL);
645         if (!subvol) {
646                 ret = -errno;
647                 error("unable to resolve %s", argv[optind]);
648                 goto out;
649         }
650
651         ret = init_root_path(&send, subvol);
652         if (ret < 0)
653                 goto out;
654
655         if (snapshot_parent != NULL) {
656                 ret = get_root_id(&send,
657                         subvol_strip_mountpoint(send.root_path, snapshot_parent),
658                         &parent_root_id);
659                 if (ret < 0) {
660                         error("could not resolve rootid for %s", snapshot_parent);
661                         goto out;
662                 }
663
664                 ret = add_clone_source(&send, parent_root_id);
665                 if (ret < 0) {
666                         error("cannot add clone source: %s", strerror(-ret));
667                         goto out;
668                 }
669         }
670
671         for (i = optind; i < argc; i++) {
672                 free(subvol);
673                 subvol = realpath(argv[i], NULL);
674                 if (!subvol) {
675                         ret = -errno;
676                         error("unable to resolve %s", argv[i]);
677                         goto out;
678                 }
679
680                 ret = find_mount_root(subvol, &mount_root);
681                 if (ret < 0) {
682                         error("find_mount_root failed on %s: %s", subvol,
683                                 strerror(-ret));
684                         goto out;
685                 }
686                 if (ret > 0) {
687                         error("%s does not belong to btrfs mount point",
688                                 subvol);
689                         ret = -EINVAL;
690                         goto out;
691                 }
692                 if (strcmp(send.root_path, mount_root) != 0) {
693                         ret = -EINVAL;
694                         error("all subvolumes must be from the same filesystem");
695                         goto out;
696                 }
697                 free(mount_root);
698
699                 ret = is_subvol_ro(&send, subvol);
700                 if (ret < 0)
701                         goto out;
702                 if (!ret) {
703                         ret = -EINVAL;
704                         error("subvolume %s is not read-only", subvol);
705                         goto out;
706                 }
707         }
708
709         if ((send_flags & BTRFS_SEND_FLAG_NO_FILE_DATA) && g_verbose > 1)
710                 if (g_verbose > 1)
711                         fprintf(stderr, "Mode NO_FILE_DATA enabled\n");
712
713         for (i = optind; i < argc; i++) {
714                 int is_first_subvol;
715                 int is_last_subvol;
716
717                 free(subvol);
718                 subvol = argv[i];
719
720                 if (g_verbose > 0)
721                         fprintf(stderr, "At subvol %s\n", subvol);
722
723                 subvol = realpath(subvol, NULL);
724                 if (!subvol) {
725                         ret = -errno;
726                         error("realpath %s failed: %s", argv[i], strerror(-ret));
727                         goto out;
728                 }
729
730                 if (!full_send && !snapshot_parent) {
731                         ret = set_root_info(&send, subvol, &root_id);
732                         if (ret < 0)
733                                 goto out;
734
735                         ret = find_good_parent(&send, root_id, &parent_root_id);
736                         if (ret < 0) {
737                                 error("parent determination failed for %lld",
738                                         root_id);
739                                 goto out;
740                         }
741                 }
742
743                 if (new_end_cmd_semantic) {
744                         /* require new kernel */
745                         is_first_subvol = (i == optind);
746                         is_last_subvol = (i == argc - 1);
747                 } else {
748                         /* be compatible to old and new kernel */
749                         is_first_subvol = 1;
750                         is_last_subvol = 1;
751                 }
752                 ret = do_send(&send, parent_root_id, is_first_subvol,
753                               is_last_subvol, subvol, send_flags);
754                 if (ret < 0)
755                         goto out;
756
757                 if (!full_send && !snapshot_parent) {
758                         /* done with this subvol, so add it to the clone sources */
759                         ret = add_clone_source(&send, root_id);
760                         if (ret < 0) {
761                                 error("cannot add clone source: %s", strerror(-ret));
762                                 goto out;
763                         }
764                         free_send_info(&send);
765                 }
766         }
767
768         ret = 0;
769
770 out:
771         free(subvol);
772         free(snapshot_parent);
773         free(send.clone_sources);
774         free_send_info(&send);
775         return !!ret;
776 }
777
778 const char * const cmd_send_usage[] = {
779         "btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol> [<subvol>...]",
780         "Send the subvolume(s) to stdout.",
781         "Sends the subvolume(s) specified by <subvol> to stdout.",
782         "<subvol> should be read-only here.",
783         "By default, this will send the whole subvolume. To do an incremental",
784         "send, use '-p <parent>'. If you want to allow btrfs to clone from",
785         "any additional local snapshots, use '-c <clone-src>' (multiple times",
786         "where applicable). You must not specify clone sources unless you",
787         "guarantee that these snapshots are exactly in the same state on both",
788         "sides, the sender and the receiver. It is allowed to omit the",
789         "'-p <parent>' option when '-c <clone-src>' options are given, in",
790         "which case 'btrfs send' will determine a suitable parent among the",
791         "clone sources itself.",
792         "\n",
793         "-e               If sending multiple subvols at once, use the new",
794         "                 format and omit the end-cmd between the subvols.",
795         "-p <parent>      Send an incremental stream from <parent> to",
796         "                 <subvol>.",
797         "-c <clone-src>   Use this snapshot as a clone source for an ",
798         "                 incremental send (multiple allowed)",
799         "-f <outfile>     Output is normally written to stdout. To write to",
800         "                 a file, use this option. An alternative would be to",
801         "                 use pipes.",
802         "--no-data        send in NO_FILE_DATA mode, Note: the output stream",
803         "                 does not contain any file data and thus cannot be used",
804         "                 to transfer changes. This mode is faster and useful to",
805         "                 show the differences in metadata.",
806         "-v|--verbose     enable verbose output to stderr, each occurrence of",
807         "                 this option increases verbosity",
808         "-q|--quiet       suppress all messages, except errors",
809         NULL
810 };