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(&times[0], &stat.st_atimespec);	/* access time		*/
607 			TIMESPEC_TO_TIMEVAL(&times[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(&times[0], &stat.st_atimespec);	/* access time		*/
694 		TIMESPEC_TO_TIMEVAL(&times[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