source: mainline/uspace/lib/fmgt/src/fmgt.c

Last change on this file was 0ce9eb8, checked in by Jiri Svoboda <jiri@…>, 2 days ago

Ask user what to do if destination file exists while copying.

  • Property mode set to 100644
File size: 8.1 KB
Line 
1/*
2 * Copyright (c) 2026 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup fmgt
30 * @{
31 */
32/** @file File management library.
33 */
34
35#include <dirent.h>
36#include <errno.h>
37#include <stdbool.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <stddef.h>
41#include <str.h>
42#include <str.h>
43#include <vfs/vfs.h>
44#include <dirent.h>
45
46#include "fmgt.h"
47#include "../private/fmgt.h"
48
49/** Create file management library instance.
50 *
51 * @param rfmgt Place to store pointer to new file management library instance
52 * @return EOK on succcess, ENOMEM if out of memory.
53 */
54errno_t fmgt_create(fmgt_t **rfmgt)
55{
56 fmgt_t *fmgt;
57
58 fmgt = calloc(1, sizeof(fmgt_t));
59 if (fmgt == NULL)
60 return ENOMEM;
61
62 fibril_mutex_initialize(&fmgt->lock);
63 fmgt->timer = fibril_timer_create(&fmgt->lock);
64 if (fmgt->timer == NULL) {
65 free(fmgt);
66 return ENOMEM;
67 }
68
69 *rfmgt = fmgt;
70 return EOK;
71}
72
73/** Create file management library instance.
74 *
75 * @param fmgt File management object
76 * @param cb Callback functions
77 * @param arg Argument to callback functions
78 * @return EOK on succcess, ENOMEM if out of memory.
79 */
80void fmgt_set_cb(fmgt_t *fmgt, fmgt_cb_t *cb, void *arg)
81{
82 fmgt->cb = cb;
83 fmgt->cb_arg = arg;
84}
85
86/** Configure whether to give immediate initial progress update.
87 *
88 * @param fmgt File management object
89 * @param enabled @c true to post and immediate initial progress update
90 */
91void fmgt_set_init_update(fmgt_t *fmgt, bool enabled)
92{
93 fmgt->do_init_update = enabled;
94}
95
96/** Destroy file management library instance.
97 *
98 * @param fmgt File management object
99 */
100void fmgt_destroy(fmgt_t *fmgt)
101{
102 (void)fibril_timer_clear(fmgt->timer);
103 fibril_timer_destroy(fmgt->timer);
104 free(fmgt);
105}
106
107/** Initialize progress counters at beginning of operation.
108 *
109 * @param fmgt File management object
110 */
111void fmgt_progress_init(fmgt_t *fmgt)
112{
113 fmgt->total_procf = 0;
114 fmgt->total_procb = 0;
115
116 fmgt->curf_procb = 0;
117 fmgt->curf_totalb = 0;
118 fmgt->curf_progr = false;
119}
120
121/** Initialize progress counters at beginning of processing a file.
122 *
123 * @param fmgt File management object
124 * @param fname File name
125 */
126void fmgt_progress_init_file(fmgt_t *fmgt, const char *fname)
127{
128 vfs_stat_t stat;
129 errno_t rc;
130
131 fmgt->curf_procb = 0;
132 fmgt->curf_totalb = 0;
133
134 rc = vfs_stat_path(fname, &stat);
135 if (rc == EOK)
136 fmgt->curf_totalb = stat.size;
137}
138
139/** Increase count of processed bytes.
140 *
141 * @param fmgt File management object
142 * @param nbytes Number of newly processed bytes
143 */
144void fmgt_progress_incr_bytes(fmgt_t *fmgt, uint64_t nbytes)
145{
146 fmgt->curf_procb += nbytes;
147 fmgt->total_procb += nbytes;
148}
149
150/** Increase count of processed files.
151 *
152 * @parma fmgt File management object
153 */
154void fmgt_progress_incr_files(fmgt_t *fmgt)
155{
156 ++fmgt->total_procf;
157}
158
159/** Get progress update report.
160 *
161 * @param fmgt File management object
162 * @param progress Place to store progress update
163 */
164static void fmgt_get_progress(fmgt_t *fmgt, fmgt_progress_t *progress)
165{
166 unsigned percent;
167
168 if (fmgt->curf_totalb > 0)
169 percent = fmgt->curf_procb * 100 / fmgt->curf_totalb;
170 else
171 percent = 100;
172
173 capa_blocks_format_buf(fmgt->curf_procb, 1, progress->curf_procb,
174 sizeof(progress->curf_procb));
175 capa_blocks_format_buf(fmgt->curf_totalb, 1, progress->curf_totalb,
176 sizeof(progress->curf_totalb));
177 snprintf(progress->curf_percent, sizeof(progress->curf_percent), "%u%%",
178 percent);
179 snprintf(progress->total_procf, sizeof(progress->total_procf), "%u",
180 fmgt->total_procf);
181 capa_blocks_format_buf(fmgt->total_procb, 1, progress->total_procb,
182 sizeof(progress->total_procb));
183}
184
185/** Give the caller progress update.
186 *
187 * @param fmgt File management object
188 */
189static void fmgt_progress_update(fmgt_t *fmgt)
190{
191 fmgt_progress_t progress;
192
193 if (fmgt->cb != NULL && fmgt->cb->progress != NULL) {
194 fmgt_get_progress(fmgt, &progress);
195 fmgt->curf_progr = true;
196 fmgt->cb->progress(fmgt->cb_arg, &progress);
197 }
198}
199
200/** Provide initial progress update (if required).
201 *
202 * The caller configures the file management object regarding whether
203 * initial updates are required.
204 *
205 * @param fmgt File management object
206 */
207void fmgt_initial_progress_update(fmgt_t *fmgt)
208{
209 if (fmgt->do_init_update)
210 fmgt_progress_update(fmgt);
211}
212
213/** Provide final progress update (if required).
214 *
215 * Final update is provided only if a previous progress update was given.
216 *
217 * @param fmgt File management object
218 */
219void fmgt_final_progress_update(fmgt_t *fmgt)
220{
221 if (fmgt->curf_progr)
222 fmgt_progress_update(fmgt);
223}
224
225/** Progress timer function.
226 *
227 * Periodically called to provide progress updates.
228 *
229 * @param arg Argument (fmgt_t *)
230 */
231static void fmgt_timer_fun(void *arg)
232{
233 fmgt_t *fmgt = (fmgt_t *)arg;
234
235 fmgt_progress_update(fmgt);
236 fibril_timer_set(fmgt->timer, 500000, fmgt_timer_fun, (void *)fmgt);
237}
238
239/** Start progress update timer.
240 *
241 * @param fmgt File management object
242 */
243void fmgt_timer_start(fmgt_t *fmgt)
244{
245 fibril_timer_set(fmgt->timer, 500000, fmgt_timer_fun, (void *)fmgt);
246}
247
248/** Stop progress update timer.
249 *
250 * @param fmgt File management object
251 */
252void fmgt_timer_stop(fmgt_t *fmgt)
253{
254 (void)fibril_timer_clear(fmgt->timer);
255}
256
257/** Query caller whether operation should be aborted.
258 *
259 * @param fmgt File management object
260 * @return @c true iff operation should be aborted
261 */
262bool fmgt_abort_query(fmgt_t *fmgt)
263{
264 if (fmgt->cb != NULL && fmgt->cb->abort_query != NULL)
265 return fmgt->cb->abort_query(fmgt->cb_arg);
266 else
267 return false;
268}
269
270/** Query caller how to recover from I/O error.
271 *
272 * @param fmgt File management object
273 * @param err I/O error report
274 * @return What error recovery action should be taken.
275 */
276fmgt_error_action_t fmgt_io_error_query(fmgt_t *fmgt,
277 fmgt_io_error_t *err)
278{
279 if (fmgt->cb != NULL && fmgt->cb->io_error_query != NULL)
280 return fmgt->cb->io_error_query(fmgt->cb_arg, err);
281 else
282 return fmgt_er_abort;
283}
284
285/** Query caller how to recover from existing destination file/directory.
286 *
287 * @param fmgt File management object
288 * @param exists File/directory exists report
289 * @return What recovery action should be taken.
290 */
291fmgt_exists_action_t fmgt_exists_query(fmgt_t *fmgt, fmgt_exists_t *exists)
292{
293 if (fmgt->cb != NULL && fmgt->cb->exists_query != NULL)
294 return fmgt->cb->exists_query(fmgt->cb_arg, exists);
295 else
296 return fmgt_exr_abort;
297}
298
299/** Return base name (without path component).
300 *
301 * @param path Pathname
302 * @return Base name without directory components
303 */
304const char *fmgt_basename(const char *path)
305{
306 const char *p;
307
308 p = str_rchr(path, '/');
309 if (p != NULL)
310 return p + 1;
311 else
312 return path;
313}
314
315/** Determine if pathname is an existing directory.
316 *
317 * @param path Pathname
318 * @return @c true if @a path exists and is a directory
319 */
320bool fmgt_is_dir(const char *path)
321{
322 vfs_stat_t stat;
323 errno_t rc;
324
325 rc = vfs_stat_path(path, &stat);
326 if (rc != EOK)
327 return false;
328
329 return stat.is_directory;
330}
331
332/** @}
333 */
Note: See TracBrowser for help on using the repository browser.