summarylogtreecommitdiffstats
path: root/0001-copy-Add-a-progress-option.patch
blob: b185da011095b6b309299ef5e46fd2487be9b8f0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
From 2d86253391a4662bef020e038308fae7f2f23de1 Mon Sep 17 00:00:00 2001
From: KoffeinFlummi <koffeinflummi@gmail.com>
Date: Tue, 2 Jun 2015 01:37:39 +0200
Subject: [PATCH 1/6] copy: Add a --progress option

* add a progress option that shows the progress, speed and estimated
  time remaining for the copying process. implies -v
---
 src/copy.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 src/copy.h |   3 ++
 src/cp.c   |  11 ++++++-
 3 files changed, 108 insertions(+), 8 deletions(-)

diff --git a/src/copy.c b/src/copy.c
index 5fe69ea..94f4512 100644
--- a/src/copy.c
+++ b/src/copy.c
@@ -21,6 +21,7 @@
 #include <assert.h>
 #include <sys/ioctl.h>
 #include <sys/types.h>
+#include <sys/time.h>
 #include <selinux/selinux.h>
 
 #if HAVE_HURD_H
@@ -195,6 +196,74 @@ create_hole (int fd, char const *name, bool punch_holes, off_t size)
   return true;
 }
 
+/* Returns a human-readable file size.
+   todo: move this somewhere more appropriate. */
+static char*
+readable_fsize(off_t size, char* buf)
+{
+  int i = 0;
+  double size_f = (double)size;
+  const char* units[] = {" B", "KB", "MB", "GB", "TB", "PB"};
+  while (size_f > 1000 && i < 5)
+    {
+      size_f /= 1024.0;
+      ++i;
+    }
+
+  sprintf(buf, "%3.2f%s", size_f, units[i]);
+  return buf;
+}
+
+
+/* Emit the current progress.
+   todo: move this somewhere more appropriate. */
+static void
+emit_progress (off_t size_done, off_t size_total, clock_t start, bool final)
+{
+  if (final)
+    size_done = size_total;
+
+  double elapsed = (clock() - start) / (double)CLOCKS_PER_SEC;
+  int done = 0;
+  if (size_total != 0)
+    done = size_done / (size_total / 100);
+
+  char str_done[512];
+  readable_fsize(size_done, str_done);
+  char str_total[512];
+  readable_fsize(size_total, str_total);
+
+  char progress_bar[] = "-------------------------";
+  for (int i = 0; i < 25; i++)
+    {
+      if (done > i*4)
+        progress_bar[i] = '#';
+    }
+
+  off_t speed = 0;
+  if (elapsed != 0)
+    speed = size_done / elapsed;
+  char str_speed[512];
+  readable_fsize(speed, str_speed);
+
+  int seconds = (size_total - size_done) / speed;
+  if (final)
+    seconds = (int)elapsed;
+  int minutes = seconds / 60;
+  seconds -= minutes * 60;
+
+  printf("\e[?25l");
+
+  if (final)
+    printf(" %3i%s [%s]   %8s / %s   %8s/s  %02i:%02i total  \n", done, "%", progress_bar,
+      str_done, str_total, str_speed, minutes, seconds);
+  else
+    printf(" %3i%s [%s]   %8s / %s   %8s/s  %02i:%02i ETA  \r", done, "%", progress_bar,
+      str_done, str_total, str_speed, minutes, seconds);
+
+  printf("\e[?25h");
+}
+
 
 /* Copy the regular file open on SRC_FD/SRC_NAME to DST_FD/DST_NAME,
    honoring the MAKE_HOLES setting and using the BUF_SIZE-byte buffer
@@ -212,13 +281,19 @@ sparse_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
              size_t hole_size, bool punch_holes,
              char const *src_name, char const *dst_name,
              uintmax_t max_n_read, off_t *total_n_read,
-             bool *last_write_made_hole)
+             bool *last_write_made_hole, bool progress)
 {
   *last_write_made_hole = false;
   *total_n_read = 0;
   bool make_hole = false;
   off_t psize = 0;
 
+  off_t size_done = 0;
+  off_t size_total = lseek(src_fd, 0, SEEK_END);
+  lseek(src_fd, 0, SEEK_SET);
+  clock_t start_time = clock();
+  clock_t last_progress = 0;
+
   while (max_n_read)
     {
       ssize_t n_read = read (src_fd, buf, MIN (max_n_read, buf_size));
@@ -265,6 +340,16 @@ sparse_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
               if (! transition)
                 psize += csize;
 
+              if (progress)
+                {
+                  size_done += psize;
+                  if (clock() - last_progress > CLOCKS_PER_SEC / 100)
+                    {
+                      last_progress = clock();
+                      emit_progress(size_done, size_total, start_time, false);
+                    }
+                }
+
               if (! prev_hole)
                 {
                   if (full_write (dest_fd, pbuf, psize) != psize)
@@ -316,6 +401,8 @@ sparse_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
          certain files in /proc or /sys with linux kernels.  */
     }
 
+  emit_progress(size_done, size_total, start_time, true);
+
   /* Ensure a trailing hole is created, so that subsequent
      calls of sparse_copy() start at the correct offset.  */
   if (make_hole && ! create_hole (dest_fd, dst_name, punch_holes, psize))
@@ -388,7 +475,7 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
              size_t hole_size, off_t src_total_size,
              enum Sparse_type sparse_mode,
              char const *src_name, char const *dst_name,
-             bool *require_normal_copy)
+             bool *require_normal_copy, bool progress)
 {
   struct extent_scan scan;
   off_t last_ext_start = 0;
@@ -507,7 +594,7 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
               if ( ! sparse_copy (src_fd, dest_fd, buf, buf_size,
                                   sparse_mode == SPARSE_ALWAYS ? hole_size: 0,
                                   true, src_name, dst_name, ext_len, &n_read,
-                                  &wrote_hole_at_eof))
+                                  &wrote_hole_at_eof, progress))
                 goto fail;
 
               dest_pos = ext_start + n_read;
@@ -999,7 +1086,7 @@ static bool
 copy_reg (char const *src_name, char const *dst_name,
           const struct cp_options *x,
           mode_t dst_mode, mode_t omitted_permissions, bool *new_dst,
-          struct stat const *src_sb)
+          struct stat const *src_sb, bool progress)
 {
   char *buf;
   char *buf_alloc = NULL;
@@ -1269,7 +1356,8 @@ copy_reg (char const *src_name, char const *dst_name,
           if (extent_copy (source_desc, dest_desc, buf, buf_size, hole_size,
                            src_open_sb.st_size,
                            make_holes ? x->sparse_mode : SPARSE_NEVER,
-                           src_name, dst_name, &normal_copy_required))
+                           src_name, dst_name, &normal_copy_required,
+                           x->progress))
             goto preserve_metadata;
 
           if (! normal_copy_required)
@@ -1285,7 +1373,7 @@ copy_reg (char const *src_name, char const *dst_name,
                          make_holes ? hole_size : 0,
                          x->sparse_mode == SPARSE_ALWAYS, src_name, dst_name,
                          UINTMAX_MAX, &n_read,
-                         &wrote_hole_at_eof))
+                         &wrote_hole_at_eof, x->progress))
         {
           return_val = false;
           goto close_src_and_dst_desc;
@@ -2614,7 +2702,7 @@ copy_internal (char const *src_name, char const *dst_name,
          used only by 'install', which POSIX does not specify and
          where DST_MODE_BITS is what's wanted.  */
       if (! copy_reg (src_name, dst_name, x, dst_mode_bits & S_IRWXUGO,
-                      omitted_permissions, &new_dst, &src_sb))
+                      omitted_permissions, &new_dst, &src_sb, x->progress))
         goto un_backup;
     }
   else if (S_ISFIFO (src_mode))
diff --git a/src/copy.h b/src/copy.h
index bff5ff8..b82536e 100644
--- a/src/copy.h
+++ b/src/copy.h
@@ -238,6 +238,9 @@ struct cp_options
   /* If true, display the names of the files before copying them. */
   bool verbose;
 
+  /* If true, display the progress in copying the current file. */
+  bool progress;
+
   /* If true, stdin is a tty.  */
   bool stdin_tty;
 
diff --git a/src/cp.c b/src/cp.c
index 0ffd12d..4f1d421 100644
--- a/src/cp.c
+++ b/src/cp.c
@@ -141,6 +141,7 @@ static struct option const long_opts[] =
   {"target-directory", required_argument, NULL, 't'},
   {"update", no_argument, NULL, 'u'},
   {"verbose", no_argument, NULL, 'v'},
+  {"progress", no_argument, NULL, 'V'},
   {GETOPT_SELINUX_CONTEXT_OPTION_DECL},
   {GETOPT_HELP_OPTION_DECL},
   {GETOPT_VERSION_OPTION_DECL},
@@ -227,6 +228,8 @@ Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.\n\
                                  than the destination file or when the\n\
                                  destination file is missing\n\
   -v, --verbose                explain what is being done\n\
+  -V, --progress               show progress, speed and estimated time\n\
+                                 remaining (implies --verbose)\n\
   -x, --one-file-system        stay on this file system\n\
 "), stdout);
       fputs (_("\
@@ -809,6 +812,7 @@ cp_option_init (struct cp_options *x)
 
   x->update = false;
   x->verbose = false;
+  x->progress = false;
 
   /* By default, refuse to open a dangling destination symlink, because
      in general one cannot do that safely, give the current semantics of
@@ -943,7 +947,7 @@ main (int argc, char **argv)
      we'll actually use backup_suffix_string.  */
   backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
 
-  while ((c = getopt_long (argc, argv, "abdfHilLnprst:uvxPRS:TZ",
+  while ((c = getopt_long (argc, argv, "abdfHilLnprst:uvVxPRS:TZ",
                            long_opts, NULL))
          != -1)
     {
@@ -1097,6 +1101,11 @@ main (int argc, char **argv)
           x.verbose = true;
           break;
 
+        case 'V':
+          x.verbose = true;
+          x.progress = true;
+          break;
+
         case 'x':
           x.one_file_system = true;
           break;
-- 
2.5.0