1 /* $NetBSD: utoppya.c,v 1.2 2006/04/03 13:30:24 martin Exp $ */ 2 3 /*- 4 * Copyright (c) 2006 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Steve C. Woodford. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 #include <sys/types.h> 40 #include <sys/stat.h> 41 #include <sys/ioctl.h> 42 43 #include <err.h> 44 #include <errno.h> 45 #include <fcntl.h> 46 #include <libgen.h> 47 #include <sysexits.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <strings.h> 51 #include <unistd.h> 52 #include <time.h> 53 #include <inttypes.h> 54 55 #include <dev/usb/utoppy.h> 56 57 #define GLOBAL 58 #include "progressbar.h" 59 60 #define _PATH_DEV_UTOPPY "/dev/utoppy0" 61 62 /* 63 * This looks weird for a reason. The toppy protocol allows for data to be 64 * transferred in 65535-byte chunks only. Anything more than this has to be 65 * split within the driver. The following value leaves enough space for the 66 * packet header plus some alignmnent slop. 67 */ 68 #define TOPPY_IO_SIZE 0xffec 69 70 static int toppy_fd; 71 72 static void cmd_df(int, char **); 73 static void cmd_ls(int, char **); 74 static void cmd_rm(int, char **); 75 static void cmd_mkdir(int, char **); 76 static void cmd_rename(int, char **); 77 static void cmd_get(int, char **); 78 static void cmd_put(int, char **); 79 80 static struct toppy_command { 81 const char *tc_cmd; 82 void (*tc_handler)(int, char **); 83 } toppy_commands[] = { 84 {"df", cmd_df}, 85 {"ls", cmd_ls}, 86 {"get", cmd_get}, 87 {"mkdir", cmd_mkdir}, 88 {"put", cmd_put}, 89 {"rename", cmd_rename}, 90 {"rm", cmd_rm}, 91 {NULL, NULL} 92 }; 93 94 static void 95 usage(void) 96 { 97 98 fprintf(stderr, "usage: %s [-f <path>] <cmd> ...\n", 99 getprogname()); 100 101 exit(EX_USAGE); 102 } 103 104 int 105 main(int argc, char *argv[]) 106 { 107 struct toppy_command *tc; 108 const char *devpath; 109 int ch; 110 111 setprogname(argv[0]); 112 devpath = _PATH_DEV_UTOPPY; 113 114 while ((ch = getopt(argc, argv, "f:")) != -1) { 115 switch (ch) { 116 case 'f': 117 devpath = optarg; 118 break; 119 120 default: 121 usage(); 122 } 123 } 124 argc -= optind; 125 argv += optind; 126 127 if (argc == 0) 128 usage(); 129 130 for (tc = toppy_commands; tc->tc_cmd != NULL; tc++) 131 if (strcasecmp(argv[0], tc->tc_cmd) == 0) 132 break; 133 134 if (tc->tc_cmd == NULL) 135 errx(EX_USAGE, "'%s' is not a valid command", argv[0]); 136 137 if ((toppy_fd = open(devpath, O_RDWR)) < 0) 138 err(EX_OSERR, "open(%s)", devpath); 139 140 (*tc->tc_handler)(argc, argv); 141 142 close(toppy_fd); 143 144 return (0); 145 } 146 147 static int 148 find_toppy_dirent(const char *path, struct utoppy_dirent *udp) 149 { 150 struct utoppy_dirent ud; 151 char *d, *b, dir[FILENAME_MAX]; 152 ssize_t l; 153 154 strncpy(dir, path, sizeof(dir)); 155 b = basename(dir); 156 d = dirname(dir); 157 if (strcmp(b, "/") == 0 || strcmp(b, ".") == 0 || strcmp(d, ".") == 0) 158 errx(EX_USAGE, "'%s' is not a valid Toppy pathname", path); 159 160 if (ioctl(toppy_fd, UTOPPYIOREADDIR, &d) < 0) 161 err(EX_OSERR, "ioctl(UTOPPYIOREADDIR, %s)", d); 162 163 if (udp == NULL) 164 udp = &ud; 165 166 while ((l = read(toppy_fd, udp, sizeof(*udp))) == sizeof(*udp)) { 167 if (strcmp(b, udp->ud_path) == 0) 168 break; 169 } 170 171 if (l < 0) 172 err(EX_OSERR, "read(TOPPYDIR, %s)", d); 173 174 if (l == 0) 175 return (0); 176 177 while (read(toppy_fd, &ud, sizeof(ud)) > 0) 178 ; 179 180 return (1); 181 } 182 183 static void 184 cmd_df(int argc, char **argv) 185 { 186 struct utoppy_stats us; 187 188 if (ioctl(toppy_fd, UTOPPYIOSTATS, &us) < 0) 189 err(EX_OSERR, "ioctl(UTOPPYIOSTATS)"); 190 191 printf("Hard Disk Size: %" PRId64 " MB\n", us.us_hdd_size / (1024 * 1024)); 192 printf("Hard Disk Free: %" PRId64 " MB\n", us.us_hdd_free / (1024 * 1024)); 193 } 194 195 static void 196 cmd_ls(int argc, char **argv) 197 { 198 struct utoppy_dirent ud; 199 struct tm *tm; 200 char *dir, *ext, dirbuf[2], ex, ft, tmbuf[32]; 201 ssize_t l; 202 203 if (argc == 1) { 204 dirbuf[0] = '/'; 205 dirbuf[1] = '\0'; 206 dir = dirbuf; 207 } else 208 if (argc == 2) 209 dir = argv[1]; 210 else 211 errx(EX_USAGE, "usage: ls [toppy-pathname]"); 212 213 if (ioctl(toppy_fd, UTOPPYIOREADDIR, &dir) < 0) 214 err(EX_OSERR, "ioctl(UTOPPYIOREADDIR, %s)", dir); 215 216 while ((l = read(toppy_fd, &ud, sizeof(ud))) == sizeof(ud)) { 217 switch (ud.ud_type) { 218 default: 219 ft = '?'; 220 break; 221 222 case UTOPPY_DIRENT_DIRECTORY: 223 ft = 'd'; 224 break; 225 226 case UTOPPY_DIRENT_FILE: 227 ft = '-'; 228 break; 229 } 230 231 if ((ext = strrchr(ud.ud_path, '.')) != NULL && 232 strcasecmp(ext, ".tap") == 0) 233 ex = 'x'; 234 else 235 ex = '-'; 236 237 tm = localtime(&ud.ud_mtime); 238 strftime(tmbuf, sizeof(tmbuf), "%b %e %G %R", tm); 239 240 printf("%crw%c %11lld %s %s\n", ft, ex, (long long)ud.ud_size, 241 tmbuf, ud.ud_path); 242 } 243 244 if (l < 0) 245 err(EX_OSERR, "read(utoppy_dirent)"); 246 } 247 248 static void 249 cmd_rm(int argc, char **argv) 250 { 251 char *path; 252 253 if (argc != 2) 254 errx(EX_USAGE, "usage: rm <toppy-pathname>"); 255 256 path = argv[1]; 257 258 if (ioctl(toppy_fd, UTOPPYIODELETE, &path) < 0) 259 err(EX_OSERR, "ioctl(UTOPPYIODELETE, %s)", path); 260 } 261 262 static void 263 cmd_mkdir(int argc, char **argv) 264 { 265 char *path; 266 267 if (argc != 2) 268 errx(EX_USAGE, "usage: mkdir <toppy-pathname>"); 269 270 path = argv[1]; 271 272 if (find_toppy_dirent(path, NULL)) 273 errx(EX_DATAERR, "'%s' already exists", path); 274 275 if (ioctl(toppy_fd, UTOPPYIOMKDIR, &path) < 0) 276 err(EX_OSERR, "ioctl(UTOPPYIOMKDIR, %s)", path); 277 } 278 279 static void 280 cmd_rename(int argc, char **argv) 281 { 282 struct utoppy_dirent ud; 283 struct utoppy_rename ur; 284 char *oldpath, *newpath, *o, *n; 285 286 if (argc != 3) 287 errx(EX_USAGE, "usage: rename <from> <to>"); 288 289 o = oldpath = argv[1]; 290 n = newpath = argv[2]; 291 292 for (o = oldpath; *o != '\0'; o++) 293 if (*o == '\\') 294 *o = '/'; 295 for (n = newpath; *n != '\0'; n++) 296 if (*n == '\\') 297 *n = '/'; 298 299 for (o = oldpath; *o && *o == '\\'; o++) 300 ; 301 for (n = newpath; *n && *n == '\\'; n++) 302 ; 303 304 if (strcmp(n, o) == 0) 305 errx(EX_DATAERR, "'%s' and '%s' refer to the same file\n", 306 oldpath, newpath); 307 308 if (find_toppy_dirent(oldpath, &ud) == 0) 309 errx(EX_DATAERR, "'%s' does not exist on the Toppy", oldpath); 310 311 if (ud.ud_type != UTOPPY_DIRENT_FILE) 312 errx(EX_DATAERR, "%s: not a regular file", oldpath); 313 314 if (find_toppy_dirent(newpath, &ud)) 315 errx(EX_DATAERR, "'%s' already exists", newpath); 316 317 ur.ur_old_path = o; 318 ur.ur_new_path = n; 319 320 if (ioctl(toppy_fd, UTOPPYIORENAME, &ur) < 0) 321 err(EX_OSERR, "ioctl(UTOPPYIORENAME, %s, %s)", oldpath, 322 newpath); 323 } 324 325 326 static void 327 init_progress(FILE *to, char *f, off_t fsize, off_t restart) 328 { 329 struct ttysize ts; 330 331 if (ioctl(fileno(to), TIOCGSIZE, &ts) == -1) 332 ttywidth = 80; 333 else 334 ttywidth = ts.ts_cols; 335 336 ttyout = to; 337 progress = 1; 338 bytes = 0; 339 filesize = fsize; 340 restart_point = restart; 341 prefix = f; 342 } 343 344 static void 345 cmd_get(int argc, char **argv) 346 { 347 struct utoppy_readfile ur; 348 struct utoppy_dirent ud; 349 struct stat st; 350 char *dst, dstbuf[FILENAME_MAX]; 351 uint8_t *buf; 352 ssize_t l; 353 size_t rv; 354 int ch, turbo_mode = 0, reget = 0, progbar = 0; 355 FILE *ofp, *to; 356 357 optind = 1; 358 optreset = 1; 359 360 while ((ch = getopt(argc, argv, "prt")) != -1) { 361 switch (ch) { 362 case 'p': 363 progbar = 1; 364 break; 365 case 'r': 366 reget = 1; 367 break; 368 case 't': 369 turbo_mode = 1; 370 break; 371 default: 372 get_usage: 373 errx(EX_USAGE, "usage: get [-prt] <toppy-pathname> " 374 "[file | directory]"); 375 } 376 } 377 argc -= optind; 378 argv += optind; 379 380 if (argc == 1) 381 dst = basename(argv[0]); 382 else 383 if (argc == 2) { 384 dst = argv[1]; 385 if (stat(dst, &st) == 0 && S_ISDIR(st.st_mode)) { 386 snprintf(dstbuf, sizeof(dstbuf), "%s/%s", dst, 387 basename(argv[0])); 388 dst = dstbuf; 389 } 390 } else 391 goto get_usage; 392 393 ur.ur_path = argv[0]; 394 ur.ur_offset = 0; 395 396 if ((buf = malloc(TOPPY_IO_SIZE)) == NULL) 397 err(EX_OSERR, "malloc(TOPPY_IO_SIZE)"); 398 399 if (strcmp(dst, "-") == 0) { 400 ofp = stdout; 401 to = stderr; 402 if (reget) 403 warnx("Ignoring -r option in combination with stdout"); 404 } else { 405 to = stdout; 406 407 if (reget) { 408 if (stat(dst, &st) < 0) { 409 if (errno != ENOENT) 410 err(EX_OSERR, "stat(%s)", dst); 411 } else 412 if (!S_ISREG(st.st_mode)) 413 errx(EX_DATAERR, "-r only works with regular " 414 "files"); 415 else 416 ur.ur_offset = st.st_size; 417 } 418 419 if ((ofp = fopen(dst, reget ? "a" : "w")) == NULL) 420 err(EX_OSERR, "fopen(%s)", dst); 421 } 422 423 if (progbar) { 424 if (find_toppy_dirent(ur.ur_path, &ud) == 0) 425 ud.ud_size = 0; 426 init_progress(to, dst, ud.ud_size, ur.ur_offset); 427 } 428 429 if (ioctl(toppy_fd, UTOPPYIOTURBO, &turbo_mode) < 0) 430 err(EX_OSERR, "ioctl(UTOPPYIOTURBO, %d)", turbo_mode); 431 432 if (ioctl(toppy_fd, UTOPPYIOREADFILE, &ur) < 0) 433 err(EX_OSERR, "ioctl(UTOPPYIOREADFILE, %s)", ur.ur_path); 434 435 if (progbar) 436 progressmeter(-1); 437 438 for (;;) { 439 while ((l = read(toppy_fd, buf, TOPPY_IO_SIZE)) < 0 && 440 errno == EINTR) 441 ; 442 443 if (l <= 0) 444 break; 445 446 rv = fwrite(buf, 1, l, ofp); 447 448 if (rv != l) { 449 if (ofp != stdout) 450 fclose(ofp); 451 progressmeter(1); 452 err(EX_OSERR, "fwrite(%s)", dst); 453 } 454 bytes += l; 455 } 456 457 if (progbar) 458 progressmeter(1); 459 460 if (ofp != stdout) 461 fclose(ofp); 462 463 if (l < 0) 464 err(EX_OSERR, "read(TOPPY: ur.ur_path)"); 465 466 free(buf); 467 } 468 469 static void 470 cmd_put(int argc, char **argv) 471 { 472 struct utoppy_writefile uw; 473 struct utoppy_dirent ud; 474 struct stat st; 475 char dstbuf[FILENAME_MAX]; 476 char *src; 477 void *buf; 478 ssize_t rv; 479 size_t l; 480 int ch, turbo_mode = 0, reput = 0, progbar = 0; 481 FILE *ifp; 482 483 optind = 1; 484 optreset = 1; 485 486 while ((ch = getopt(argc, argv, "prt")) != -1) { 487 switch (ch) { 488 case 'p': 489 progbar = 1; 490 break; 491 case 'r': 492 reput = 1; 493 break; 494 case 't': 495 turbo_mode = 1; 496 break; 497 default: 498 put_usage: 499 errx(EX_USAGE, "usage: put [-prt] <local-pathname> " 500 "<toppy-pathname>"); 501 } 502 } 503 argc -= optind; 504 argv += optind; 505 506 if (argc != 2) 507 goto put_usage; 508 509 src = argv[0]; 510 uw.uw_path = argv[1]; 511 512 if (stat(src, &st) < 0) 513 err(EX_OSERR, "%s", src); 514 515 if (!S_ISREG(st.st_mode)) 516 errx(EX_DATAERR, "'%s' is not a regular file", src); 517 518 uw.uw_size = st.st_size; 519 uw.uw_mtime = st.st_mtime; 520 uw.uw_offset = 0; 521 522 if (find_toppy_dirent(uw.uw_path, &ud)) { 523 if (ud.ud_type == UTOPPY_DIRENT_DIRECTORY) { 524 snprintf(dstbuf, sizeof(dstbuf), "%s/%s", uw.uw_path, 525 basename(src)); 526 uw.uw_path = dstbuf; 527 } else 528 if (ud.ud_type != UTOPPY_DIRENT_FILE) 529 errx(EX_DATAERR, "'%s' is not a regular file.", 530 uw.uw_path); 531 else 532 if (reput) { 533 if (ud.ud_size > uw.uw_size) 534 errx(EX_DATAERR, "'%s' is already larger than " 535 "'%s'", uw.uw_path, src); 536 537 uw.uw_size -= ud.ud_size; 538 uw.uw_offset = ud.ud_size; 539 } 540 } 541 542 if ((buf = malloc(TOPPY_IO_SIZE)) == NULL) 543 err(EX_OSERR, "malloc(TOPPY_IO_SIZE)"); 544 545 if ((ifp = fopen(src, "r")) == NULL) 546 err(EX_OSERR, "fopen(%s)", src); 547 548 if (ioctl(toppy_fd, UTOPPYIOTURBO, &turbo_mode) < 0) 549 err(EX_OSERR, "ioctl(UTOPPYIOTURBO, %d)", turbo_mode); 550 551 if (ioctl(toppy_fd, UTOPPYIOWRITEFILE, &uw) < 0) 552 err(EX_OSERR, "ioctl(UTOPPYIOWRITEFILE, %s)", uw.uw_path); 553 554 if (progbar) 555 init_progress(stdout, src, st.st_size, uw.uw_offset); 556 557 if (progbar) 558 progressmeter(-1); 559 560 while ((l = fread(buf, 1, TOPPY_IO_SIZE, ifp)) > 0) { 561 rv = write(toppy_fd, buf, l); 562 if (rv != l) { 563 fclose(ifp); 564 if (progbar) 565 progressmeter(1); 566 err(EX_OSERR, "write(TOPPY: %s)", uw.uw_path); 567 } 568 bytes += l; 569 } 570 571 if (progbar) 572 progressmeter(1); 573 574 if (ferror(ifp)) 575 err(EX_OSERR, "fread(%s)", src); 576 577 fclose(ifp); 578 free(buf); 579 } 580