1 /* $NetBSD$ */
2
3 /*
4 * File "udfclient.c" is part of the UDFclient toolkit.
5 * File $Id: udfclient.c,v 1.104 2016/04/25 21:28:00 reinoud Exp $ $Name: $
6 *
7 * Copyright (c) 2003, 2004, 2005, 2006, 2011
8 * Reinoud Zandijk <reinoud@netbsd.org>
9 * All rights reserved.
10 *
11 * The UDFclient toolkit is distributed under the Clarified Artistic Licence.
12 * A copy of the licence is included in the distribution as
13 * `LICENCE.clearified.artistic' and a copy of the licence can also be
14 * requested at the GNU foundantion's website.
15 *
16 * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 */
30
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <assert.h>
36 #include <fcntl.h>
37 #include <unistd.h>
38 #include <limits.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <sys/time.h>
42 #include <errno.h>
43 #include "udf.h"
44 #include "udf_bswap.h"
45
46 /* switches */
47
48 /* #define DEBUG(a) (a) */
49 #define DEBUG(a) if (0) { a; }
50
51
52 #ifndef MAX
53 #define MAX(a,b) ((a)>(b)?(a):(b))
54 #define MIN(a,b) ((a)<(b)?(a):(b))
55 #endif
56
57
58 /* include timeval to timespec conversion macro's for systems that don't provide them */
59 #ifndef TIMEVAL_TO_TIMESPEC
60 # define TIMEVAL_TO_TIMESPEC(tv, ts) do { \
61 (ts)->tv_sec = (tv)->tv_sec; \
62 (ts)->tv_nsec = (tv)->tv_usec * 1000; \
63 } while (/*CONSTCOND*/0)
64 #endif
65 #ifndef TIMESPEC_TO_TIMEVAL
66 # define TIMESPEC_TO_TIMEVAL(tv, ts) do { \
67 (tv)->tv_sec = (ts)->tv_sec; \
68 (tv)->tv_usec = (ts)->tv_nsec / 1000; \
69 } while (/*CONSTCOND*/0)
70 #endif
71
72
73 /* include the dump parts ... in order to get a more sane splitting */
74 extern void udf_dump_alive_sets(void);
75
76
77 /* globals */
78 extern int udf_verbose;
79 extern int uscsilib_verbose;
80 int read_only;
81
82
83 #define MAX_ARGS 100
84
85
86 struct curdir {
87 char *name;
88 struct udf_mountpoint *mountpoint; /* foreign */
89 struct udf_node *udf_node; /* foreign */
90 struct hash_entry *udf_mountpoint_entry; /* `current' mountpoint entry */
91 } curdir;
92
93
94
95 /*
96 * XXX
97 * FTP client; de volumes vooraan zetten in de VFS ; evt. in meerdere subdirs.
98 * general file name format
99 *
100 * /volset:pri:log/udfpath
101 * of
102 * /volset/pri/log/udfpath/
103 *
104 * XXX
105 */
106
udfclient_readdir(struct udf_node * udf_node,struct uio * result_uio,int * eof_res)107 int udfclient_readdir(struct udf_node *udf_node, struct uio *result_uio, int *eof_res) {
108 struct dirent entry;
109 struct udf_mountpoint *mountable;
110
111 assert(result_uio);
112 if (!udf_node) {
113 /* mountables */
114 /* XXX result_uio needs to be long enough to hold all mountables!!!! XXX */
115 SLIST_FOREACH(mountable, &udf_mountables, all_next) {
116 strcpy(entry.d_name, mountable->mount_name);
117 entry.d_type = DT_DIR;
118 uiomove(&entry, sizeof(struct dirent), result_uio);
119 }
120 if (eof_res) *eof_res = 1;
121 return 0;
122 }
123
124 /* intree nodes : pass on to udf_readdir */
125 return udf_readdir(udf_node, result_uio, eof_res);
126 }
127
128
129 /* VOP_LOOKUP a-like */
udfclient_lookup(struct udf_node * dir_node,struct udf_node ** resnode,char * name)130 int udfclient_lookup(struct udf_node *dir_node, struct udf_node **resnode, char *name) {
131 struct udf_mountpoint *mountable;
132 struct fileid_desc *fid;
133 struct long_ad udf_icbptr;
134 int lb_size, found, error;
135
136 assert(resnode);
137 assert(name);
138 *resnode = NULL;
139 if (!dir_node) {
140 /* mountables */
141 SLIST_FOREACH(mountable, &udf_mountables, all_next) {
142 if (strcmp(mountable->mount_name, name) == 0) {
143 /* found `root' of a mountable */
144 *resnode = mountable->rootdir_node;
145 return 0;
146 }
147 }
148 return ENOENT;
149 }
150
151 /* intree nodes : pass on to udf_lookup_name_in_dir */
152 lb_size = dir_node->udf_log_vol->lb_size;
153 fid = malloc(lb_size);
154 assert(fid);
155
156 error = udf_lookup_name_in_dir(dir_node, name, strlen(name), &udf_icbptr, fid, &found);
157 if (!error) {
158 error = ENOENT;
159 if (found)
160 error = udf_readin_udf_node(dir_node, &udf_icbptr, fid, resnode);
161 }
162
163 free(fid);
164 return error;
165 }
166
167
udfclient_getattr(struct udf_node * udf_node,struct stat * stat)168 int udfclient_getattr(struct udf_node *udf_node, struct stat *stat) {
169 int error;
170
171 error = 0;
172 if (udf_node) {
173 error = udf_getattr(udf_node, stat);
174 /* print? */
175 if (error) fprintf(stderr, "Can't stat file\n");
176 } else {
177 /* dummy entry for `root' in VFS */
178 stat->st_mode = 0744 | S_IFDIR;
179 stat->st_size = 0;
180 stat->st_uid = 0;
181 stat->st_gid = 0;
182 }
183
184 return error;
185 }
186
187
188 /* higher level of lookup; walk tree */
189 /* results in a `held'/locked node upto `root' */
udfclient_lookup_pathname(struct udf_node * cur_node,struct udf_node ** res_node,char * restpath_given)190 int udfclient_lookup_pathname(struct udf_node *cur_node, struct udf_node **res_node, char *restpath_given) {
191 struct udf_node *sub_node;
192 char *restpath, *next_element, *slashpos, *pathpos;
193 int error;
194
195 assert(restpath_given);
196 restpath = strdup(restpath_given);
197
198 /* start at root */
199 *res_node = NULL;
200 pathpos = restpath;
201 assert(*pathpos == '/');
202 pathpos++; /* strip leading '/' */
203
204 next_element = pathpos;
205 while (next_element && (strlen(next_element) > 0)) {
206 /* determine next slash position */
207 slashpos = strchr(next_element, '/');
208 if (slashpos) *slashpos++ = 0;
209
210 error = udfclient_lookup(cur_node, &sub_node, next_element);
211 if (error) {
212 free(restpath);
213 return error;
214 }
215
216 /* advance */
217 cur_node = sub_node;
218 next_element = slashpos;
219 }
220 /* we are at the end; return result */
221 *res_node = cur_node;
222 free(restpath);
223 return 0;
224 }
225
226
udfclient_realpath(char * cur_path,char * relpath,char ** leaf)227 char *udfclient_realpath(char *cur_path, char *relpath, char **leaf) {
228 char *resultpath, *here, *pos;
229
230 resultpath = calloc(1, sizeof(cur_path) + sizeof(relpath) +1024);
231 assert(resultpath);
232
233 strcpy(resultpath, "/");
234 strcat(resultpath, cur_path);
235 strcat(resultpath, "/");
236
237 /* check if we are going back to `root' */
238 if (relpath && *relpath == '/') {
239 strcpy(resultpath, "");
240 }
241 strcat(resultpath, relpath);
242 /* now clean up the resulting string by removing double slashes */
243 here = resultpath;
244 while (*here) {
245 pos = here; while (strncmp(pos, "//", 2) == 0) pos++;
246 if (pos != here) strcpy(here, pos);
247 here++;
248 }
249
250 /* remove '.' and '..' sequences */
251 here = resultpath;
252 while (*here) {
253 /* printf("transformed to %s\n", resultpath); */
254 /* check for internal /./ and trailing /. */
255 if (strncmp(here, "/./", 3) == 0) {
256 strcpy(here+1, here + 3);
257 continue;
258 }
259 if (strcmp(here, "/.")==0) {
260 strcpy(here+1, here + 2);
261 continue;
262 }
263 if (strncmp(here, "/../", 4) == 0) {
264 strcpy(here+1, here + 4);
265 /* go for the parent */
266 pos = here-1; while (*pos && *pos != '/') pos--; pos++;
267 strcpy(pos, here+1);
268 here = pos;
269 continue;
270 }
271 if (strcmp(here, "/..")==0) {
272 strcpy(here+1, here + 3);
273 /* go for the parent */
274 pos = here-1; while (*pos && *pos != '/') pos--; pos++;
275 strcpy(pos, here+1);
276 here = pos;
277 continue;
278 }
279 here++;
280 }
281 if (leaf) {
282 /* find the leaf name */
283 here = resultpath;
284 while (*here) {
285 if (strncmp(here, "/", 1) == 0)
286 *leaf = here+1;
287 here++;
288 }
289 }
290
291 return resultpath;
292 }
293
294
print_dir_entry(struct udf_node * udf_node,char * name)295 static void print_dir_entry(struct udf_node *udf_node, char *name) {
296 struct stat stat;
297 uint64_t size;
298 int mode, this_mode, uid, gid;
299 int error;
300
301 error = udfclient_getattr(udf_node, &stat);
302 if (error) return;
303
304 size = stat.st_size;
305 mode = stat.st_mode;
306 uid = stat.st_uid;
307 gid = stat.st_gid;
308
309 if (mode & S_IFDIR) printf("d"); else printf("-");
310 mode = mode & 511;
311
312 this_mode = (mode >> 6) & 7;
313 printf("%c%c%c", "----rrrr"[this_mode & 4], "--www"[this_mode & 2], "-x"[this_mode & 1]);
314 this_mode = (mode >> 3) & 7;
315 printf("%c%c%c", "----rrrr"[this_mode & 4], "--www"[this_mode & 2], "-x"[this_mode & 1]);
316 this_mode = mode & 7;
317 printf("%c%c%c", "----rrrr"[this_mode & 4], "--www"[this_mode & 2], "-x"[this_mode & 1]);
318
319 printf(" %5d %5d %10"PRIu64" %s\n", gid, uid, size, name);
320 }
321
322
323 #define LS_SUBTREE_DIR_BUFFER_SIZE (16*1024)
udfclient_ls(int args,char * arg1)324 void udfclient_ls(int args, char *arg1) {
325 struct udf_node *udf_node, *entry_node;
326 uint8_t *buffer;
327 struct uio dir_uio;
328 struct iovec dir_uiovec;
329 struct dirent *dirent;
330 struct stat stat;
331 uint32_t pos;
332 int eof;
333 char *node_name, *leaf_name;
334 int error;
335
336 if (args > 1) {
337 printf("Syntax: ls [file | dir]\n");
338 return;
339 }
340 if (args == 0) arg1 = "";
341
342 node_name = udfclient_realpath(curdir.name, arg1, &leaf_name);
343
344 error = udfclient_lookup_pathname(NULL, &udf_node, node_name);
345 if (error) {
346 fprintf(stderr, "%s : %s\n", arg1, strerror(error));
347 free(node_name);
348 return;
349 }
350
351 error = udfclient_getattr(udf_node, &stat);
352 if (stat.st_mode & S_IFDIR) {
353 printf("Directory listing of %s\n", udf_node ? leaf_name : "/");
354
355 /* start at the start of the directory */
356 dir_uio.uio_offset = 0;
357 dir_uio.uio_iov = &dir_uiovec;
358 dir_uio.uio_iovcnt = 1;
359 buffer = calloc(1, LS_SUBTREE_DIR_BUFFER_SIZE);
360 if (!buffer) return;
361
362 do {
363 dir_uiovec.iov_base = buffer;
364 dir_uiovec.iov_len = LS_SUBTREE_DIR_BUFFER_SIZE;
365 dir_uio.uio_resid = LS_SUBTREE_DIR_BUFFER_SIZE;
366 dir_uio.uio_rw = UIO_WRITE;
367
368 error = udfclient_readdir(udf_node, &dir_uio, &eof);
369 if (error) {
370 fprintf(stderr, "error during readdir: %s\n", strerror(error));
371 break;
372 }
373 pos = 0;
374 while (pos < LS_SUBTREE_DIR_BUFFER_SIZE - dir_uio.uio_resid) {
375 dirent = (struct dirent *) (buffer + pos);
376 error = udfclient_lookup(udf_node, &entry_node, dirent->d_name);
377 print_dir_entry(entry_node, dirent->d_name);
378
379 pos += sizeof(struct dirent);
380 }
381 } while (!eof);
382 free(buffer);
383 } else {
384 print_dir_entry(udf_node, leaf_name);
385 }
386 free(node_name);
387 }
388 #undef LS_SUBTREE_DIR_BUFFER_SIZE
389
390
udfclient_pwd(int args)391 void udfclient_pwd(int args) {
392 char pwd[1024];
393 char *res;
394
395 if (args) {
396 printf("Syntax: pwd\n");
397 return;
398 }
399 res = getcwd(pwd, 1024);
400 assert(res);
401 printf("UDF working directory is %s\n", curdir.name);
402 printf("Current FS working directory %s\n", pwd);
403 }
404
405
udfclient_print_free_amount(char * prefix,uint64_t value,uint64_t max_value)406 static void udfclient_print_free_amount(char *prefix, uint64_t value, uint64_t max_value) {
407 printf("%s %10"PRIu64" Kb (%3"PRIu64" %%) (%8.2f Mb) (%5.2f Gb)\n",
408 prefix, value/1024, (100*value)/max_value, (double) value/(1024*1024), (double) value/(1024*1024*1024));
409 }
410
411
udfclient_free(int args)412 void udfclient_free(int args) {
413 struct udf_part_mapping *part_mapping;
414 struct udf_partition *udf_partition;
415 struct udf_log_vol *udf_log_vol;
416 struct logvol_desc *lvd;
417 uint64_t part_size, unalloc_space, freed_space;
418 uint64_t total_space, free_space, await_alloc_space;
419 uint32_t lb_size;
420 int part_num;
421
422 if (args) {
423 printf("Syntax: free\n");
424 return;
425 }
426
427 if (!curdir.udf_node || !(udf_log_vol = curdir.udf_node->udf_log_vol)) {
428 printf("Can only report free space in UDF mountpoints\n");
429 return;
430 }
431
432 lb_size = udf_log_vol->lb_size;
433 // sector_size = udf_log_vol->sector_size;
434
435 lvd = udf_log_vol->log_vol;
436 udf_dump_id("Logical volume ", 128, lvd->logvol_id, &lvd->desc_charset);
437
438 total_space = udf_log_vol->total_space;
439 free_space = udf_log_vol->free_space;
440 await_alloc_space = udf_log_vol->await_alloc_space;
441
442 SLIST_FOREACH(part_mapping, &udf_log_vol->part_mappings, next_mapping) {
443 part_num = part_mapping->udf_virt_part_num;
444 udf_logvol_vpart_to_partition(udf_log_vol, part_num, NULL, &udf_partition);
445 assert(udf_partition);
446
447 unalloc_space = udf_partition->free_unalloc_space;
448 freed_space = udf_partition->free_freed_space;
449 part_size = udf_partition->part_length;
450
451 switch (part_mapping->udf_part_mapping_type) {
452 case UDF_PART_MAPPING_PHYSICAL :
453 printf("\tphysical partition %d\n", part_num);
454 printf("\t\t%8"PRIu64" K (%"PRIu64" pages) size\n", part_size/1024, part_size / lb_size);
455 printf("\t\t%8"PRIu64" K (%"PRIu64" pages) unallocated\n", unalloc_space/1024, unalloc_space / lb_size);
456 printf("\t\t%8"PRIu64" K (%"PRIu64" pages) freed\n", freed_space/1024, freed_space / lb_size);
457 break;
458 case UDF_PART_MAPPING_VIRTUAL :
459 printf("\tvirtual partition mapping %d\n", part_num);
460 printf("\t\tnot applicable\n");
461 break;
462 case UDF_PART_MAPPING_SPARABLE :
463 printf("\tsparable partition %d\n", part_num);
464 printf("\t\t%8"PRIu64" K (%"PRIu64" pages) size\n", part_size/1024, part_size / lb_size);
465 printf("\t\t%8"PRIu64" K (%"PRIu64" pages) unallocated\n", unalloc_space/1024, unalloc_space / lb_size);
466 printf("\t\t%8"PRIu64" K (%"PRIu64" pages) freed\n", freed_space/1024, freed_space / lb_size);
467 break;
468 case UDF_PART_MAPPING_META :
469 printf("\tmetadata 'partition' %d\n", part_num);
470 printf("\t\t%8"PRIu64" K (%"PRIu64" pages) unallocated\n", unalloc_space/1024, unalloc_space / lb_size);
471 printf("\t\t%8"PRIu64" K (%"PRIu64" pages) freed\n", freed_space/1024, freed_space / lb_size);
472 break;
473 case UDF_PART_MAPPING_ERROR :
474 printf("\terror partiton %d\n", part_num);
475 break;
476 default:
477 break;
478 }
479 }
480 printf("\n");
481 udfclient_print_free_amount("\tConfirmed free space ", free_space, total_space);
482 udfclient_print_free_amount("\tAwaiting allocation ", await_alloc_space, total_space);
483 udfclient_print_free_amount("\tEstimated free space ", free_space - await_alloc_space, total_space);
484 udfclient_print_free_amount("\tEstimated total used ", total_space - free_space + await_alloc_space, total_space);
485 printf("\n");
486 udfclient_print_free_amount("\tTotal size ", total_space, total_space);
487 }
488
489
udfclient_cd(int args,char * arg1)490 void udfclient_cd(int args, char *arg1) {
491 struct udf_node *udf_node;
492 struct stat stat;
493 char *node_name, *new_curdir_name;
494 int error;
495
496 if (args > 1) {
497 printf("Syntax: cd [dir]\n");
498 return;
499 }
500
501 new_curdir_name = udfclient_realpath(curdir.name, arg1, NULL);
502
503 node_name = strdup(new_curdir_name); /* working copy */
504 error = udfclient_lookup_pathname(NULL, &udf_node, node_name);
505 if (error) {
506 fprintf(stderr, "%s : %s\n", arg1, strerror(error));
507 free(new_curdir_name);
508 free(node_name);
509 return;
510 }
511
512 error = udfclient_getattr(udf_node, &stat);
513 if (stat.st_mode & S_IFDIR) {
514 free(curdir.name);
515 curdir.name = new_curdir_name;
516 curdir.udf_node = udf_node;
517 free(node_name);
518
519 udfclient_pwd(0);
520 } else {
521 fprintf(stderr, "%s is not a directory\n", arg1);
522 free(new_curdir_name);
523 free(node_name);
524 }
525 }
526
527
udfclient_lcd(int args,char * arg1)528 void udfclient_lcd(int args, char *arg1) {
529 char pwd[1024];
530 char *res;
531
532 if (args > 1) {
533 printf("Syntax: lcd [dir]\n");
534 return;
535 }
536
537 if (strcmp(arg1, "" )==0) arg1 = getenv("HOME");
538 if (strcmp(arg1, "~")==0) arg1 = getenv("HOME");
539
540 if (chdir(arg1)) {
541 fprintf(stderr, "While trying to go to %s :", arg1);
542 perror("");
543 }
544 res = getcwd(pwd, 1024);
545 assert(res);
546 printf("Changing local directory to %s\n", pwd);
547 }
548
549
udfclient_lls(int args)550 void udfclient_lls(int args) {
551 if (args) {
552 printf("Syntax: lls\n");
553 return;
554 }
555 if (system("/bin/ls")) {
556 perror("While listing current directory\n");
557 }
558 }
559
560
getmtime(void)561 uint64_t getmtime(void) {
562 struct timeval tp;
563
564 gettimeofday(&tp, NULL);
565 return 1000000*tp.tv_sec + tp.tv_usec;
566 }
567
568
569
udfclient_get_file(struct udf_node * udf_node,char * fullsrcname,char * fulldstname)570 int udfclient_get_file(struct udf_node *udf_node, char *fullsrcname, char *fulldstname) {
571 struct uio file_uio;
572 struct iovec file_iov;
573 struct stat stat;
574 struct timeval times[2];
575 uint64_t file_length;
576 uint64_t start, now, then, eta;
577 uint64_t cur_speed, avg_speed, data_transfered;
578 uint64_t file_block_size, file_transfer_size, written;
579 uint8_t *file_block;
580 char cur_txt[32], avg_txt[32], eta_txt[32];
581 int fileh, len;
582 int notok, error;
583
584 assert(udf_node);
585 assert(fullsrcname);
586 assert(strlen(fullsrcname) >= 1);
587
588 error = 0;
589
590 /* terminal directory node? */
591 error = udfclient_getattr(udf_node, &stat);
592 if (stat.st_mode & S_IFDIR) {
593 len = strlen(fulldstname);
594 if (strcmp(fulldstname + len -2, "/.") == 0)
595 fulldstname[len-2] = 0;
596 if (strcmp(fulldstname + len -3, "/..") == 0)
597 return 0;
598
599 error = mkdir(fulldstname, (udf_node->stat.st_mode) & 07777);
600 if (!error) {
601 /* set owner attribute and times; access permissions allready set on creation.*/
602 notok = chown(fulldstname, stat.st_uid, stat.st_gid);
603 if (notok && (udf_verbose > UDF_VERBLEV_ACTIONS))
604 fprintf(stderr, "failed to set owner of directory, ignoring\n");
605
606 TIMESPEC_TO_TIMEVAL(×[0], &stat.st_atimespec); /* access time */
607 TIMESPEC_TO_TIMEVAL(×[1], &stat.st_mtimespec); /* modification time */
608 notok = utimes(fulldstname, times);
609 if (notok)
610 fprintf(stderr, "failed to set times on directory, ignoring\n");
611 }
612 if (error)
613 fprintf(stderr, "While creating directory `%s' : %s\n", fulldstname, strerror(errno));
614
615 return 0;
616 }
617
618 /* terminal file node; setting mode correctly */
619 fileh = open(fulldstname, O_WRONLY | O_CREAT | O_TRUNC, udf_node->stat.st_mode);
620 if (fileh >= 0) {
621 file_length = udf_node->stat.st_size;
622 file_block_size = 256*1024; /* block read in length */
623 file_block = malloc(file_block_size);
624 if (!file_block) {
625 printf("Out of memory claiming file buffer\n");
626 return ENOMEM;
627 }
628
629 /* move to uio_newuio(struct uio *uio) with fixed length uio_iovcnt? */
630 bzero(&file_uio, sizeof(struct uio));
631 file_uio.uio_rw = UIO_WRITE; /* WRITE into this space */
632 file_uio.uio_iovcnt = 1;
633 file_uio.uio_iov = &file_iov;
634
635 start = getmtime();
636 then = now = start;
637 eta = data_transfered = 0;
638 strcpy(avg_txt, "---"); strcpy(cur_txt, "---"); strcpy(eta_txt, "---");
639
640 file_uio.uio_offset = 0; /* begin at the start */
641 do {
642 /* fill in IO vector space; reuse blob file_block over and over */
643 file_transfer_size = MIN(file_block_size, file_length - file_uio.uio_offset);
644 file_uio.uio_resid = file_transfer_size;
645 file_uio.uio_iov->iov_base = file_block;
646 file_uio.uio_iov->iov_len = file_block_size;
647
648 error = udf_read_file_part_uio(udf_node, fullsrcname, UDF_C_USERDATA, &file_uio);
649 if (error) {
650 fprintf(stderr, "While retrieving file block : %s\n", strerror(error));
651 printf("\n\n\n"); /* XXX */
652 break;
653 }
654
655 written = write(fileh, file_block, file_transfer_size);
656 assert(written == file_transfer_size);
657
658 if ((getmtime() - now > 1000000) || ((uint64_t) file_uio.uio_offset >= file_length)) {
659 if (strlen(fulldstname) < 45) {
660 printf("\r%-45s ", fulldstname);
661 } else {
662 printf("\r...%-42s ", fulldstname + strlen(fulldstname)-42);
663 }
664 printf("%10"PRIu64" / %10"PRIu64" bytes ", (uint64_t) file_uio.uio_offset, (uint64_t) file_length);
665 if (file_length) printf("(%3d%%) ", (int) (100.0*(float) file_uio.uio_offset / file_length));
666
667 then = now;
668 now = getmtime();
669 cur_speed = 0;
670 avg_speed = 0;
671 if (now-start > 0) avg_speed = (1000000 * file_uio.uio_offset) / (now-start);
672 if (now-then > 0) cur_speed = (1000000 * (file_uio.uio_offset - data_transfered)) / (now-then);
673 if (avg_speed > 0) eta = (file_length - file_uio.uio_offset) / avg_speed;
674 data_transfered = file_uio.uio_offset;
675
676 strcpy(avg_txt, "---"); strcpy(cur_txt, "---"); strcpy(eta_txt, "---");
677 if (avg_speed > 0) sprintf(avg_txt, "%d", (int32_t) avg_speed/1000);
678 if (cur_speed > 0) sprintf(cur_txt, "%d", (int32_t) cur_speed/1000);
679 if (eta > 0) sprintf(eta_txt, "%02d:%02d:%02d", (int) (eta/3600), (int) (eta/60) % 60, (int) eta % 60);
680
681 printf("%6s KB/s (%6s KB/s) ETA %s", avg_txt, cur_txt, eta_txt);
682 fflush(stdout);
683 }
684 } while ((uint64_t) file_uio.uio_offset < file_length);
685 printf(" finished\n");
686 free(file_block);
687
688 /* set owner attribute and times; access permissions allready set on creation.*/
689 notok = fchown(fileh, stat.st_uid, stat.st_gid);
690 if (notok && (udf_verbose > UDF_VERBLEV_ACTIONS))
691 fprintf(stderr, "failed to set owner of file, ignoring\n");
692
693 TIMESPEC_TO_TIMEVAL(×[0], &stat.st_atimespec); /* access time */
694 TIMESPEC_TO_TIMEVAL(×[1], &stat.st_mtimespec); /* modification time */
695 notok = futimes(fileh, times);
696 if (notok)
697 fprintf(stderr, "failed to set times on directory, ignoring\n");
698
699 close(fileh);
700 } else {
701 printf("Help! can't open file %s for output\n", fulldstname);
702 }
703
704 return error;
705 }
706
707
708 #define GET_SUBTREE_DIR_BUFFER_SIZE (16*1024)
udfclient_get_subtree(struct udf_node * udf_node,char * srcprefix,char * dstprefix,int recurse,uint64_t * total_size)709 void udfclient_get_subtree(struct udf_node *udf_node, char *srcprefix, char *dstprefix, int recurse, uint64_t *total_size) {
710 struct uio dir_uio;
711 struct iovec dir_iovec;
712 uint8_t *buffer;
713 uint32_t pos;
714 char fullsrcpath[1024], fulldstpath[1024]; /* XXX arbitrary length XXX */
715 struct dirent *dirent;
716 struct stat stat;
717 struct udf_node *entry_node;
718 struct fileid_desc *fid;
719 struct long_ad udf_icbptr;
720 int lb_size, eof;
721 int found, isdot, isdotdot, error;
722
723 if (!udf_node)
724 return;
725
726 udf_node->hold++;
727 error = udfclient_getattr(udf_node, &stat);
728 if ((stat.st_mode & S_IFDIR) && recurse) {
729 buffer = malloc(GET_SUBTREE_DIR_BUFFER_SIZE);
730 if (!buffer) {
731 udf_node->hold--;
732 return;
733 }
734
735 lb_size = udf_node->udf_log_vol->lb_size;
736 fid = malloc(lb_size);
737 assert(fid);
738
739 /* recurse into this directory */
740 dir_uio.uio_offset = 0; /* begin at start */
741 do {
742 dir_iovec.iov_base = buffer;
743 dir_iovec.iov_len = GET_SUBTREE_DIR_BUFFER_SIZE;
744 dir_uio.uio_resid = GET_SUBTREE_DIR_BUFFER_SIZE;
745 dir_uio.uio_iovcnt = 1;
746 dir_uio.uio_iov = &dir_iovec;
747 dir_uio.uio_rw = UIO_WRITE;
748
749 error = udf_readdir(udf_node, &dir_uio, &eof);
750 pos = 0;
751 while (pos < GET_SUBTREE_DIR_BUFFER_SIZE - dir_uio.uio_resid) {
752 dirent = (struct dirent *) (buffer + pos);
753
754 sprintf(fullsrcpath, "%s/%s", srcprefix, dirent->d_name);
755 sprintf(fulldstpath, "%s/%s", dstprefix, dirent->d_name);
756
757 /* looking for '.' or '..' ? */
758 isdot = (strcmp(dirent->d_name, "." ) == 0);
759 isdotdot = (strcmp(dirent->d_name, "..") == 0);
760
761 pos += sizeof(struct dirent); /* XXX variable size dirents possible XXX */
762
763 if (isdotdot)
764 continue;
765
766 if (isdot) {
767 /* hack */
768 udfclient_get_subtree(udf_node, fullsrcpath, fulldstpath, 0, total_size);
769 continue;
770 }
771
772 error = udf_lookup_name_in_dir(udf_node, dirent->d_name, DIRENT_NAMLEN(dirent), &udf_icbptr, fid, &found);
773 if (!error) {
774 error = ENOENT;
775 if (found)
776 error = udf_readin_udf_node(udf_node, &udf_icbptr, fid, &entry_node);
777 }
778
779 if (!error)
780 udfclient_get_subtree(entry_node, fullsrcpath, fulldstpath, 1, total_size);
781
782 }
783 } while (!eof);
784
785 udf_node->hold--;
786 free(buffer);
787 free(fid);
788 return;
789 }
790
791 /* leaf node : prefix is complete name but with `/' prefix */
792 if (*srcprefix == '/') srcprefix++;
793 error = udfclient_get_file(udf_node, srcprefix, dstprefix);
794 udf_node->hold--;
795 if (!error)
796 *total_size += udf_node->stat.st_size;
797 }
798 #undef GET_SUBTREE_DIR_BUFFER_SIZE
799
800
udfclient_get(int args,char * arg1,char * arg2)801 void udfclient_get(int args, char *arg1, char *arg2) {
802 struct udf_node *udf_node;
803 char *source_name, *target_name;
804 uint64_t start, now, totalsize, avg_speed;
805 int error;
806
807 if (args > 2) {
808 printf("Syntax: get remote [local]\n");
809 return;
810 }
811
812 source_name = arg1;
813 target_name = arg1;
814 if (args == 2)
815 target_name = arg2;
816
817 /* source name gets substituted */
818 source_name = udfclient_realpath(curdir.name, source_name, NULL);
819 DEBUG(printf("Attempting to retrieve %s\n", source_name));
820
821 error = udfclient_lookup_pathname(NULL, &udf_node, source_name);
822 if (error) {
823 fprintf(stderr, "%s : %s\n", arg1, strerror(error));
824 free(source_name);
825 return;
826 }
827
828 /* get the file/dir tree */
829 totalsize = 0;
830 start = getmtime();
831 udfclient_get_subtree(udf_node, source_name, target_name, 1, &totalsize);
832 now = getmtime();
833 if (now-start > 0) {
834 avg_speed = (1000000 * totalsize) / (now-start);
835 printf("A total of %d kb transfered at an overal average of %d kb/sec\n", (uint32_t) (totalsize/1024), (uint32_t) (avg_speed/1024));
836 } else {
837 printf("Transfered %d kb\n", (uint32_t)(totalsize/1024));
838 }
839
840 /* bugalert: not releasing target_name for its not substituted */
841 free(source_name);
842 }
843
844
845
udfclient_mget(int args,char * argv[])846 void udfclient_mget(int args, char *argv[]) {
847 struct udf_node *udf_node;
848 uint64_t start, now, totalsize, avg_speed;
849 char *node_name, *source_name, *target_name;
850 int arg, error;
851
852 if (args == 0) {
853 printf("Syntax: mget (file | dir)*\n");
854 return;
855 }
856
857 /* retrieve the series of file/dir trees and measure time/seed */
858 totalsize = 0;
859 start = getmtime();
860
861 /* process all args */
862 arg = 0;
863 node_name = NULL;
864 while (args) {
865 source_name = target_name = argv[arg];
866
867 node_name = udfclient_realpath(curdir.name, source_name, NULL);
868 DEBUG(printf("Attempting to retrieve %s\n", node_name));
869
870 error = udfclient_lookup_pathname(NULL, &udf_node, node_name);
871 printf("%d: mget trying %s\n", error, node_name);
872 if (!error) {
873 udfclient_get_subtree(udf_node, source_name, target_name, 1, &totalsize);
874 }
875
876 if (node_name) {
877 free(node_name);
878 node_name = NULL;
879 }
880
881 if (error) break; /* TODO continuation flag? */
882
883 /* advance */
884 arg++;
885 args--;
886 }
887
888 now = getmtime();
889 if (now-start > 0) {
890 avg_speed = (1000000 * totalsize) / (now-start);
891 printf("A total of %d kb transfered at an overal average of %d kb/sec\n", (uint32_t) (totalsize/1024), (uint32_t) (avg_speed/1024));
892 } else {
893 printf("Transfered %d kb\n", (uint32_t) (totalsize/1024));
894 }
895
896 if (node_name)
897 free(node_name);
898 }
899
900
udfclient_put_file(struct udf_node * udf_node,char * fullsrcname,char * fulldstname)901 int udfclient_put_file(struct udf_node *udf_node, char *fullsrcname, char *fulldstname) {
902 struct uio file_uio;
903 struct iovec file_iov;
904 uint64_t file_length;
905 uint64_t start, now, then, eta;
906 uint64_t cur_speed, avg_speed, data_transfered;
907 uint64_t file_block_size, file_transfer_size;
908 uint8_t *file_block;
909 char cur_txt[32], avg_txt[32], eta_txt[32];
910 int fileh;
911 int error, printed;
912
913 assert(udf_node);
914 assert(fullsrcname);
915
916 DEBUG(printf("Attempting to write %s\n", fullsrcname));
917
918 fileh = open(fullsrcname, O_RDONLY, 0666);
919 if (fileh == -1) {
920 fprintf(stderr, "Can't open local file %s for reading: %s\n", fullsrcname, strerror(errno));
921 return ENOENT;
922 }
923
924 /* get file length */
925 file_length = lseek(fileh, 0, SEEK_END);
926 lseek(fileh, 0, SEEK_SET);
927
928 /* check if file will fit; give it a bit of slack space until the space issue is found and fixed */
929 if (udf_node->udf_log_vol->free_space < file_length + udf_node->udf_log_vol->await_alloc_space + UDF_MINFREE_LOGVOL) {
930 return ENOSPC;
931 }
932
933 /* allocate file block to transfer file with */
934 file_block_size = 128*1024;
935 file_block = malloc(file_block_size);
936 if (!file_block) {
937 fprintf(stderr, "Out of memory claiming file buffer\n");
938 return ENOMEM;
939 }
940
941 /* move to uio_newuio(struct uio *uio) with fixed length uio_iovcnt? */
942 bzero(&file_uio, sizeof(struct uio));
943 file_uio.uio_rw = UIO_READ; /* READ from this space */
944 file_uio.uio_iovcnt = 1;
945 file_uio.uio_iov = &file_iov;
946
947 /* ------------ */
948 start = getmtime();
949 then = now = start;
950 eta = data_transfered = 0;
951 printed = 0;
952 strcpy(avg_txt, "---"); strcpy(cur_txt, "---"); strcpy(eta_txt, "---");
953 /* ------------ */
954
955 error = 0;
956 error = udf_truncate_node(udf_node, 0);
957 while (!error && ((uint64_t) file_uio.uio_offset < file_length)) {
958 file_transfer_size = MIN(file_block_size, file_length - file_uio.uio_offset);
959
960 error = read(fileh, file_block, file_transfer_size);
961 if (error<0) {
962 fprintf(stderr, "While reading in file block for writing : %s\n", strerror(errno));
963 error = errno;
964 break;
965 }
966
967 file_uio.uio_resid = file_transfer_size;
968 file_uio.uio_iov->iov_base = file_block;
969 file_uio.uio_iov->iov_len = file_block_size;
970
971 error = udf_write_file_part_uio(udf_node, fullsrcname, UDF_C_USERDATA, &file_uio);
972 if (error) {
973 fprintf(stderr, "\nError while writing file : %s\n", strerror(error));
974 break;
975 }
976
977 /* ------------ */
978 if ((getmtime() - now > 1000000) || ((uint64_t) file_uio.uio_offset >= file_length)) {
979 printed = 1;
980 if (strlen(fulldstname) < 45) {
981 printf("\r%-45s ", fulldstname);
982 } else {
983 printf("\r...%-42s ", fulldstname+strlen(fulldstname)-42);
984 }
985 printf("%10"PRIu64" / %10"PRIu64" bytes ", (uint64_t) file_uio.uio_offset, (uint64_t) file_length);
986 if (file_length) printf("(%3d%%) ", (int) (100.0*(float) file_uio.uio_offset / file_length));
987
988 then = now;
989 now = getmtime();
990 cur_speed = 0;
991 avg_speed = 0;
992 if (now-start > 0) avg_speed = (1000000 * file_uio.uio_offset) / (now-start);
993 if (now-then > 0) cur_speed = (1000000 * (file_uio.uio_offset - data_transfered)) / (now-then);
994 if (avg_speed > 0) eta = (file_length - file_uio.uio_offset) / avg_speed;
995 data_transfered = file_uio.uio_offset;
996
997 strcpy(avg_txt, "---"); strcpy(cur_txt, "---"); strcpy(eta_txt, "---");
998 if (avg_speed > 0) sprintf(avg_txt, "%d", (int32_t) avg_speed/1024);
999 if (cur_speed > 0) sprintf(cur_txt, "%d", (int32_t) cur_speed/1024);
1000 if (eta > 0) sprintf(eta_txt, "%02d:%02d:%02d", (int) (eta/3600), (int) (eta/60) % 60, (int) eta % 60);
1001 printf("%6s KB/s (%6s KB/s) ETA %s", avg_txt, cur_txt, eta_txt);
1002 fflush(stdout);
1003 }
1004 /* ------------ */
1005 }
1006 if (!error && printed) printf(" finished\n");
1007
1008 close(fileh);
1009 free(file_block);
1010
1011 return error;
1012 }
1013
1014
udfclient_put_subtree(struct udf_node * parent_node,char * srcprefix,char * srcname,char * dstprefix,char * dstname,uint64_t * totalsize)1015 int udfclient_put_subtree(struct udf_node *parent_node, char *srcprefix, char *srcname, char *dstprefix, char *dstname, uint64_t *totalsize) {
1016 struct udf_node *file_node, *dir_node;
1017 struct dirent *dirent;
1018 struct stat stat;
1019 DIR *dir;
1020 char fullsrcpath[1024], fulldstpath[1024];
1021 int error;
1022
1023 sprintf(fullsrcpath, "%s/%s", srcprefix, srcname);
1024 sprintf(fulldstpath, "%s/%s", dstprefix, dstname);
1025
1026 /* stat source file */
1027 bzero(&stat, sizeof(struct stat));
1028 error = lstat(fullsrcpath, &stat);
1029 if (error) {
1030 error = errno; /* lstat symantics; returns -1 on error */
1031 fprintf(stderr, "Can't stat file/dir \"%s\"! : %s\n", fullsrcpath, strerror(error));
1032 return error;
1033 }
1034
1035 /* test if `srcname' refers to a directory */
1036 dir = opendir(fullsrcpath);
1037 if (dir) {
1038 error = udfclient_lookup(parent_node, &dir_node, dstname);
1039 if (error) {
1040 DEBUG(printf("Create dir %s on UDF\n", fulldstpath));
1041 error = udf_create_directory(parent_node, dstname, &stat, &dir_node);
1042 if (error) {
1043 closedir(dir);
1044 fprintf(stderr, "UDF: couldn't create new directory %s : %s\n", dstname, strerror(error));
1045 return error;
1046 }
1047 }
1048
1049 dir_node->hold++;
1050 dirent = readdir(dir);
1051 while (dirent) {
1052 if (strcmp(dirent->d_name, ".") && strcmp(dirent->d_name, "..")) {
1053 /* skip `.' and ',,' */
1054 error = udfclient_put_subtree(dir_node, fullsrcpath, dirent->d_name, fulldstpath, dirent->d_name, totalsize);
1055 if (error) break;
1056 }
1057 dirent = readdir(dir);
1058 }
1059 closedir(dir);
1060 dir_node->hold--;
1061 return error;
1062 }
1063
1064 /* leaf node : prefix is complete name but with `/' prefix */
1065 DEBUG(printf("Put leaf: %s\n", fulldstpath));
1066
1067 error = udfclient_lookup(parent_node, &file_node, dstname);
1068 if (!file_node) {
1069 error = udf_create_file(parent_node, dstname, &stat, &file_node);
1070 if (error) {
1071 fprintf(stderr, "UDF: couldn't add new file entry in directory %s for %s : %s\n", dstprefix, dstname, strerror(error));
1072 return error;
1073 }
1074 }
1075 file_node->hold++;
1076 error = udfclient_put_file(file_node, fullsrcpath, fulldstpath);
1077 file_node->hold--;
1078
1079 if (error) fprintf(stderr, "UDF: Couldn't write file %s : %s\n", fulldstpath, strerror(error));
1080 if (error) udf_remove_file(parent_node, file_node, dstname);
1081
1082 if (!error) *totalsize += file_node->stat.st_size;
1083
1084 return error;
1085 }
1086
1087
udfclient_put(int args,char * arg1,char * arg2)1088 void udfclient_put(int args, char *arg1, char *arg2) {
1089 struct udf_node *curdir_node;
1090 uint64_t start, now, totalsize, avg_speed;
1091 char *source_name, *target_name;
1092 int error;
1093
1094 if (args > 2) {
1095 printf("Syntax: put source [destination]\n");
1096 return;
1097 }
1098
1099 if (read_only) {
1100 printf("Modifying this filingsystem is prevented; use -W flag to enable writing on your own risk!\n");
1101 return;
1102 }
1103
1104 error = udfclient_lookup_pathname(NULL, &curdir_node, curdir.name);
1105 if (error) {
1106 printf("Current directory not found?\n");
1107 return;
1108 }
1109 DEBUG(printf("Attempting to copy %s\n", arg1));
1110
1111
1112 /* determine source and destination entities */
1113 source_name = arg1;
1114 target_name = arg1;
1115 if (args == 2)
1116 target_name = arg2;
1117
1118 /* writeout file/dir tree and measure the time/speed */
1119 totalsize = 0;
1120 start = getmtime();
1121 error = udfclient_put_subtree(curdir_node, ".", source_name, ".", target_name, &totalsize);
1122 now = getmtime();
1123 if (now-start > 0) {
1124 avg_speed = (1000000 * totalsize) / (now-start);
1125 printf("A total of %d kb transfered at an overal average of %d kb/sec\n", (uint32_t) (totalsize/1024), (uint32_t) (avg_speed/1024));
1126 } else {
1127 printf("Transfered %d kb\n", (uint32_t) (totalsize/1024));
1128 }
1129 }
1130
1131
1132 /* args start at position 0 of argv */
udfclient_mput(int args,char ** argv)1133 void udfclient_mput(int args, char **argv) {
1134 struct udf_node *curdir_node;
1135 uint64_t start, now, totalsize, avg_speed;
1136 char *source_name, *target_name;
1137 int arg, error;
1138
1139 if (args == 0) {
1140 printf("Syntax: mput (file | dir)*\n");
1141 return;
1142 }
1143
1144 if (read_only) {
1145 printf("Modifying this filingsystem is prevented; use -W flag to enable writing on your own risk!\n");
1146 return;
1147 }
1148
1149 error = udfclient_lookup_pathname(NULL, &curdir_node, curdir.name);
1150 if (error) {
1151 printf("Current directory not found?\n");
1152 return;
1153 }
1154
1155 /* writeout file/dir trees and measure the time/speed */
1156 totalsize = 0;
1157 start = getmtime();
1158
1159 /* process all args */
1160 arg = 0;
1161 while (args) {
1162 source_name = target_name = argv[arg];
1163 error = udfclient_put_subtree(curdir_node, ".", source_name, ".", target_name, &totalsize);
1164 if (error) {
1165 fprintf(stderr, "While writing file %s : %s\n", source_name, strerror(error));
1166 break; /* TODO continuation flag? */
1167 }
1168
1169 /* advance */
1170 arg++;
1171 args--;
1172 }
1173
1174 now = getmtime();
1175 if (now-start > 0) {
1176 avg_speed = (1000000 * totalsize) / (now-start);
1177 printf("A total of %d kb transfered at an overal average of %d kb/sec\n", (uint32_t)(totalsize/1024), (uint32_t)(avg_speed/1024));
1178 } else {
1179 printf("Transfered %d kb\n", (uint32_t)(totalsize/1024));
1180 }
1181
1182 }
1183
1184
1185
udfclient_trunc(int args,char * arg1,char * arg2)1186 void udfclient_trunc(int args, char *arg1, char *arg2) {
1187 struct udf_node *udf_node;
1188 char *node_name;
1189 uint64_t length;
1190 int error;
1191
1192 if (args != 2) {
1193 printf("Syntax: trunc file length\n");
1194 return;
1195 }
1196 length = strtoll(arg2, NULL, 10);
1197
1198 node_name = udfclient_realpath(curdir.name, arg1, NULL);
1199 error = udfclient_lookup_pathname(NULL, &udf_node, node_name);
1200 if (error || !udf_node) {
1201 printf("Can only truncate existing file!\n");
1202 free(node_name);
1203 return;
1204 }
1205
1206 udf_truncate_node(udf_node, length);
1207
1208 free(node_name);
1209 }
1210
1211
udfclient_sync(void)1212 void udfclient_sync(void) {
1213 struct udf_discinfo *udf_disc;
1214
1215 SLIST_FOREACH(udf_disc, &udf_discs_list, next_disc) {
1216 udf_sync_disc(udf_disc);
1217 }
1218 }
1219
1220
1221 #define RM_SUBTREE_DIR_BUFFER_SIZE (32*1024)
udfclient_rm_subtree(struct udf_node * parent_node,struct udf_node * dir_node,char * name,char * full_parent_name)1222 int udfclient_rm_subtree(struct udf_node *parent_node, struct udf_node *dir_node, char *name, char *full_parent_name) {
1223 struct uio dir_uio;
1224 struct iovec dir_iovec;
1225 uint8_t *buffer;
1226 uint32_t pos;
1227 char *fullpath;
1228 struct dirent *dirent;
1229 struct stat stat;
1230 struct udf_node *entry_node;
1231 struct fileid_desc *fid;
1232 struct long_ad udf_icbptr;
1233 int lb_size, eof, found, isdot, isdotdot;
1234 int error;
1235
1236 if (!dir_node)
1237 return ENOENT;
1238
1239 error = udfclient_getattr(dir_node, &stat);
1240 if (stat.st_mode & S_IFDIR) {
1241 buffer = malloc(RM_SUBTREE_DIR_BUFFER_SIZE);
1242 if (!buffer)
1243 return ENOSPC;
1244 lb_size = dir_node->udf_log_vol->lb_size;
1245 fid = malloc(lb_size);
1246 if (!fid) {
1247 free(buffer);
1248 return ENOSPC;
1249 }
1250
1251 /* recurse into this directory */
1252 dir_uio.uio_offset = 0; /* begin at start */
1253 do {
1254 dir_iovec.iov_base = buffer;
1255 dir_iovec.iov_len = RM_SUBTREE_DIR_BUFFER_SIZE;
1256 dir_uio.uio_resid = RM_SUBTREE_DIR_BUFFER_SIZE;
1257 dir_uio.uio_iovcnt = 1;
1258 dir_uio.uio_iov = &dir_iovec;
1259 dir_uio.uio_rw = UIO_WRITE;
1260
1261 error = udf_readdir(dir_node, &dir_uio, &eof);
1262 pos = 0;
1263 while (pos < RM_SUBTREE_DIR_BUFFER_SIZE - dir_uio.uio_resid) {
1264 dirent = (struct dirent *) (buffer + pos);
1265 pos += sizeof(struct dirent); /* XXX variable size dirents possible XXX */
1266
1267 /* looking for '.' or '..' ? */
1268 isdot = (strcmp(dirent->d_name, "." ) == 0);
1269 isdotdot = (strcmp(dirent->d_name, "..") == 0);
1270
1271 if (isdot || isdotdot)
1272 continue;
1273
1274 error = udf_lookup_name_in_dir(dir_node, dirent->d_name, DIRENT_NAMLEN(dirent), &udf_icbptr, fid, &found);
1275 if (!error) {
1276 error = ENOENT;
1277 if (found)
1278 error = udf_readin_udf_node(dir_node, &udf_icbptr, fid, &entry_node);
1279 }
1280 if (error)
1281 break;
1282
1283 error = udfclient_getattr(entry_node, &stat);
1284 if (error)
1285 break;
1286
1287 /* check if the direntry is a directory or a file */
1288 if (stat.st_mode & S_IFDIR) {
1289 fullpath = malloc(strlen(full_parent_name) + strlen(dirent->d_name)+2);
1290 if (fullpath) {
1291 sprintf(fullpath, "%s/%s", full_parent_name, dirent->d_name);
1292 error = udfclient_rm_subtree(dir_node, entry_node, dirent->d_name, fullpath);
1293 } else {
1294 error = ENOMEM;
1295 }
1296 free(fullpath);
1297 } else {
1298 error = udf_remove_file(dir_node, entry_node, dirent->d_name);
1299 if (!error)
1300 printf("rm %s/%s\n", full_parent_name, dirent->d_name);
1301 }
1302 if (error)
1303 break;
1304 }
1305 } while (!eof);
1306
1307 free(buffer);
1308 free(fid);
1309
1310 /* leaving directory -> delete directory itself */
1311 if (!error) {
1312 error = udf_remove_directory(parent_node, dir_node, name);
1313 if (!error) printf("rmdir %s/%s\n", full_parent_name, name);
1314 }
1315 return error;
1316 }
1317
1318 return ENOTDIR;
1319 }
1320 #undef RM_SUBTREE_DIR_BUFFER_SIZE
1321
1322
1323
udfclient_rm(int args,char * argv[])1324 void udfclient_rm(int args, char *argv[]) {
1325 struct udf_node *remove_node, *parent_node;
1326 struct stat stat;
1327 char *target_name, *leaf_name, *full_parent_name;
1328 int error, len, arg;
1329
1330 if (args == 0) {
1331 printf("Syntax: rm (file | dir)*\n");
1332 return;
1333 }
1334
1335 /* process all args; effectively multiplying an `rm' command */
1336 arg = 0;
1337 while (args) {
1338 leaf_name = argv[arg];
1339
1340 /* lookup node; target_name gets substituted */
1341 target_name = udfclient_realpath(curdir.name, leaf_name, &leaf_name);
1342 error = udfclient_lookup_pathname(NULL, &remove_node, target_name);
1343 if (error || !remove_node) {
1344 printf("rm %s : %s\n", target_name, strerror(error));
1345 free(target_name);
1346 return; /* TODO continuation flag */
1347 /* continue; */
1348 }
1349
1350 full_parent_name = udfclient_realpath(target_name, "..", NULL);
1351 error = udfclient_lookup_pathname(NULL, &parent_node, full_parent_name);
1352 if (error || !parent_node) {
1353 printf("rm %s : parent lookup failed : %s\n", target_name, strerror(error));
1354 free(target_name);
1355 free(full_parent_name);
1356 return; /* TODO continuation flag */
1357 /* continue; */
1358 }
1359
1360 error = udfclient_getattr(remove_node, &stat);
1361 if (!error) {
1362 if (stat.st_mode & S_IFDIR) {
1363 len = strlen(target_name);
1364 if (target_name[len-1] == '/') target_name[len-1] = '\0';
1365 error = udfclient_rm_subtree(parent_node, remove_node, leaf_name, target_name);
1366 } else {
1367 error = udf_remove_file(parent_node, remove_node, leaf_name);
1368 if (!error) printf("rm %s/%s\n", full_parent_name, leaf_name);
1369 }
1370 }
1371 if (error)
1372 fprintf(stderr, "While removing file/dir : %s\n", strerror(error));
1373
1374 free(target_name);
1375 free(full_parent_name);
1376
1377 if (error)
1378 break; /* TODO continuation flag */
1379
1380 /* advance */
1381 arg++;
1382 args--;
1383 }
1384 }
1385
1386
udfclient_mv(int args,char * from,char * to)1387 void udfclient_mv(int args, char *from, char *to) {
1388 struct udf_node *rename_me, *present, *old_parent, *new_parent;
1389 char *rename_from_name, *rename_to_name, *old_parent_name, *new_parent_name;
1390 int error;
1391
1392 if (args != 2) {
1393 printf("Syntax: mv source destination\n");
1394 return;
1395 }
1396
1397 /* `from' gets substituted by its leaf name */
1398 rename_from_name = udfclient_realpath(curdir.name, from, &from);
1399 error = udfclient_lookup_pathname(NULL, &rename_me, rename_from_name);
1400 if (error || !rename_me) {
1401 printf("Can't find file/dir to be renamed\n");
1402 free(rename_from_name);
1403 return;
1404 }
1405
1406 old_parent_name = udfclient_realpath(rename_from_name, "..", NULL);
1407 error = udfclient_lookup_pathname(NULL, &old_parent, old_parent_name);
1408 if (error || !old_parent) {
1409 printf("Can't determine rootdir of renamed file?\n");
1410 free(rename_from_name);
1411 free(old_parent_name);
1412 return;
1413 }
1414
1415 /* `to' gets substituted by its leaf name */
1416 rename_to_name = udfclient_realpath(curdir.name, to, &to);
1417 udfclient_lookup_pathname(NULL, &present, rename_to_name);
1418 new_parent_name = udfclient_realpath(rename_to_name, "..", NULL);
1419 error = udfclient_lookup_pathname(NULL, &new_parent, new_parent_name);
1420 if (error || !new_parent) {
1421 printf("Can't determine rootdir of destination\n");
1422 free(rename_from_name);
1423 free(rename_to_name);
1424 free(old_parent_name);
1425 free(new_parent_name);
1426 return;
1427 }
1428
1429 error = udf_rename(old_parent, rename_me, from, new_parent, present, to);
1430 if (error) {
1431 printf("Can't move file or directory: %s\n", strerror(error));
1432 return;
1433 }
1434
1435 free(rename_from_name);
1436 free(rename_to_name);
1437 free(old_parent_name);
1438 free(new_parent_name);
1439 }
1440
1441
udfclient_mkdir(int args,char * arg1)1442 void udfclient_mkdir(int args, char *arg1) {
1443 struct stat stat;
1444 struct udf_node *udf_node, *parent_node;
1445 char *full_create_name, *dirname, *parent_name;
1446 int error;
1447
1448 if (args != 1) {
1449 printf("Syntax: mkdir dir\n");
1450 return;
1451 }
1452
1453 /* get full name of dir to be created */
1454 full_create_name = udfclient_realpath(curdir.name, arg1, &dirname);
1455 parent_name = udfclient_realpath(full_create_name, "..", NULL);
1456 error = udfclient_lookup_pathname(NULL, &parent_node, parent_name);
1457 if (error || !parent_node) {
1458 printf("Can't determine directory the new directory needs to be created in %d <%s> <%s> <%s>\n", error, parent_name, full_create_name, curdir.name);
1459 free(full_create_name);
1460 free(parent_name);
1461 return;
1462 }
1463
1464 bzero(&stat, sizeof(struct stat));
1465 stat.st_uid = UINT_MAX;
1466 stat.st_gid = UINT_MAX;
1467 stat.st_mode = 0755 | S_IFDIR; /* don't forget this! */
1468
1469 error = udf_create_directory(parent_node, dirname, &stat, &udf_node);
1470 if (error) {
1471 printf("Can't create directory %s : %s\n", arg1, strerror(error));
1472 }
1473
1474 free(full_create_name);
1475 free(parent_name);
1476 }
1477
1478
1479 /* `line' gets more and more messed up in the proces */
udfclient_get_one_arg(char * line,char ** result)1480 char *udfclient_get_one_arg(char *line, char **result) {
1481 unsigned char chr, limiter;
1482 char *end_arg;
1483
1484 *result = NULL;
1485
1486 /* get prepending whitespace */
1487 while (*line && (*line <= ' ')) line++;
1488
1489 chr= '\0';
1490 limiter = ' ';
1491 if (*line == '"') {
1492 line++;
1493 limiter = '"';
1494 }
1495
1496 *result = line;
1497
1498 while (*line) {
1499 chr = *line;
1500 if (chr && (chr < ' ')) chr = ' ';
1501 if (chr == 0 || chr == limiter) {
1502 break;
1503 } else {
1504 *line = chr;
1505 }
1506 line++;
1507 }
1508 end_arg = line;
1509
1510 if (chr == limiter) line++;
1511
1512 /* get appended whitespace */
1513 while (*line && (*line <= ' ')) line++;
1514
1515 *end_arg = 0;
1516
1517 return line;
1518 }
1519
1520
udfclient_get_args(char * line,char * argv[])1521 int udfclient_get_args(char *line, char *argv[]) {
1522 int arg, args;
1523
1524 for (arg = 0; arg < MAX_ARGS+1; arg++) {
1525 argv[arg] = "";
1526 }
1527
1528 /* get all arguments */
1529 args = 0;
1530 while (args < MAX_ARGS+1) {
1531 line = udfclient_get_one_arg(line, &argv[args]);
1532 args++;
1533 if (!*line) {
1534 return args;
1535 }
1536 }
1537
1538 printf("UDFclient implementation limit: too many arguments\n");
1539 return 0;
1540 }
1541
1542
udfclient_interact(void)1543 void udfclient_interact(void) {
1544 int args, params;
1545 char *cmd;
1546 char *argv[MAX_ARGS+1];
1547 char line[4096];
1548
1549 udfclient_pwd(0);
1550 while (1) {
1551 printf("UDF> ");
1552 clearerr(stdin);
1553
1554 *line = 0;
1555 (void) fgets(line, 4096, stdin);
1556
1557 if ((*line == 0) && feof(stdin)) {
1558 printf("quit\n");
1559 return;
1560 }
1561
1562 args = udfclient_get_args(line, argv);
1563 cmd = argv[0];
1564
1565 params = args -1;
1566 if (args) {
1567 if (strcmp(cmd, "")==0) continue;
1568
1569 if (strcmp(cmd, "ls")==0) {
1570 udfclient_ls(params, argv[1]);
1571 continue;
1572 }
1573 if (strcmp(cmd, "cd")==0) {
1574 udfclient_cd(params, argv[1]);
1575 continue;
1576 }
1577 if (strcmp(cmd, "lcd")==0) {
1578 udfclient_lcd(params, argv[1]);
1579 continue;
1580 }
1581 if (strcmp(cmd, "lls")==0) {
1582 udfclient_lls(params);
1583 continue;
1584 }
1585 if (strcmp(cmd, "pwd")==0) {
1586 udfclient_pwd(params);
1587 continue;
1588 }
1589 if (strcmp(cmd, "free")==0) {
1590 udfclient_free(params);
1591 continue;
1592 }
1593 if (strcmp(cmd, "get")==0) {
1594 udfclient_get(params, argv[1], argv[2]);
1595 continue;
1596 }
1597 if (strcmp(cmd, "mget")==0) {
1598 udfclient_mget(params, argv + 1);
1599 continue;
1600 }
1601 if (strcmp(cmd, "put")==0) {
1602 /* can get destination file/dir (one day) */
1603 udfclient_put(params, argv[1], argv[2]);
1604 continue;
1605 }
1606 if (strcmp(cmd, "mput")==0) {
1607 udfclient_mput(params, argv + 1);
1608 continue;
1609 }
1610 if (strcmp(cmd, "trunc")==0) {
1611 udfclient_trunc(params, argv[1], argv[2]);
1612 continue;
1613 }
1614 if (strcmp(cmd, "mkdir")==0) {
1615 udfclient_mkdir(params, argv[1]);
1616 continue;
1617 }
1618 if (strcmp(cmd, "rm")==0) {
1619 udfclient_rm(params, argv + 1);
1620 continue;
1621 }
1622 if (strcmp(cmd, "mv")==0) {
1623 udfclient_mv(params, argv[1], argv[2]);
1624 continue;
1625 }
1626 if (strcmp(cmd, "sync")==0) {
1627 udfclient_sync();
1628 continue;
1629 }
1630 if (strcmp(cmd, "help")==0) {
1631 printf("Selected commands available (use \" pair for filenames with spaces) :\n"
1632 "ls [file | dir]\tlists the UDF directory\n"
1633 "cd [dir]\t\tchange current UDF directory\n"
1634 "lcd [dir]\t\tchange current directory\n"
1635 "lls\t\t\tlists current directory\n"
1636 "pwd\t\t\tdisplay current directories\n"
1637 "free\t\t\tdisplay free space on disc\n"
1638 "get source [dest]\tretrieve a file / directory from disc\n"
1639 "mget (file | dir)*\tretrieve set of files / directories\n"
1640 "put source [dest]\twrite a file / directory to disc\n"
1641 "mput (file | dir)*\twrite a set of files / directories\n"
1642 "trunc file length\ttrunc file to length\n"
1643 "mkdir dir\t\tcreate directory\n"
1644 "rm (file | dir)*\tdelete set of files / directories\n"
1645 "mv source dest\t\trename a file (limited)\n"
1646 "sync\t\t\tsync filingsystem\n"
1647 "quit\t\t\texits program\n"
1648 "exit\t\t\talias for quit\n"
1649 );
1650 continue;
1651 }
1652 if (strcmp(cmd, "quit")==0 ||
1653 strcmp(cmd, "exit")==0) {
1654 return;
1655 }
1656 printf("\nUnknown command %s\n", cmd);
1657 }
1658 }
1659 }
1660
1661
usage(char * program)1662 int usage(char *program) {
1663 fprintf(stderr, "Usage: %s [options] devicename [devicename]*)\n", program);
1664 fprintf(stderr, "-u level UDF system verbose level\n"
1665 "-r range use only selected sessions like -3,5,7 or 6-\n"
1666 "-W allow writing (temporary flag)\n"
1667 "-F force mount writable when marked dirty (use with cause)\n"
1668 "-b blocksize use alternative sectorsize; use only on files/discs\n"
1669 "-D debug/verbose SCSI command errors\n"
1670 "-s byteswap read sectors (for PVRs)\n"
1671 );
1672 return 1;
1673 }
1674
1675
1676 extern char *optarg;
1677 extern int optind;
1678 extern int optreset;
1679
1680
main(int argc,char * argv[])1681 int main(int argc, char *argv[]) {
1682 struct udf_discinfo *disc, *next_disc;
1683 uint32_t alt_sector_size;
1684 char *progname, *range;
1685 int flag, mnt_flags;
1686 int error;
1687
1688 progname = argv[0];
1689 if (argc == 1) {
1690 return usage(progname);
1691 }
1692
1693 /* be a bit more verbose */
1694 udf_verbose = UDF_VERBLEV_ACTIONS;
1695 uscsilib_verbose= 0;
1696 mnt_flags = UDF_MNT_RDONLY;
1697 range = NULL;
1698 alt_sector_size = 0;
1699 while ((flag = getopt(argc, argv, "u:Dr:WFb:s")) != -1) {
1700 switch (flag) {
1701 case 'u' :
1702 udf_verbose = atoi(optarg);
1703 break;
1704 case 'D' :
1705 uscsilib_verbose = 1;
1706 break;
1707 case 'r' :
1708 range = strdup(optarg);
1709 if (udf_check_session_range(range)) {
1710 fprintf(stderr, "Invalid range %s\n", range);
1711 return usage(progname);
1712 }
1713 break;
1714 case 'W' :
1715 mnt_flags &= ~UDF_MNT_RDONLY;
1716 break;
1717 case 'F' :
1718 mnt_flags |= UDF_MNT_FORCE;
1719 break;
1720 case 'b' :
1721 alt_sector_size = atoi(optarg);
1722 break;
1723 case 's' :
1724 mnt_flags |= UDF_MNT_BSWAP;
1725 break;
1726 default :
1727 return usage(progname);
1728 }
1729 }
1730 argv += optind;
1731 argc -= optind;
1732
1733 if (argc == 0) return usage(progname);
1734
1735 if (!(mnt_flags & UDF_MNT_RDONLY)) {
1736 printf("--------------------------------\n");
1737 printf("WARNING: writing enabled, use on own risc\n");
1738 printf("\t* DONT cancel program or data-loss might occure\n");
1739 printf("\t* set dataspace userlimits very high when writing thousands of files\n");
1740 printf("\nEnjoy your writing!\n");
1741 printf("--------------------------------\n\n\n");
1742 printf("%c", 7); fflush(stdout); sleep(1); printf("%c", 7); fflush(stdout); sleep(1); printf("%c", 7); fflush(stdout);
1743 }
1744
1745 /* all other arguments are devices */
1746 udf_init();
1747 while (argc) {
1748 printf("Opening device %s\n", *argv);
1749 error = udf_mount_disc(*argv, range, alt_sector_size, mnt_flags, &disc);
1750 if (error) {
1751 fprintf(stderr, "Can't open my device; bailing out : %s\n", strerror(error));
1752 exit(1);
1753 }
1754 if (read_only) disc->recordable = 0;
1755 if (read_only) disc->rewritable = 0;
1756
1757 argv++; argc--;
1758 if (udf_verbose) printf("\n\n");
1759 }
1760
1761 printf("\n");
1762 printf("Resulting list of alive sets :\n\n");
1763 udf_dump_alive_sets();
1764
1765 /* interactive part */
1766 bzero(&curdir, sizeof(struct curdir));
1767 curdir.mountpoint = NULL;
1768 curdir.name = strdup("/");
1769 udfclient_ls(0, "");
1770
1771 udfclient_interact();
1772
1773 /* close part */
1774 printf("Closing discs\n");
1775 disc = SLIST_FIRST(&udf_discs_list);
1776 while (disc) {
1777 next_disc = SLIST_NEXT(disc, next_disc);
1778
1779 udf_dismount_disc(disc);
1780
1781 disc = next_disc;
1782 }
1783
1784 return 0;
1785 }
1786
1787