1// Copyright 2018 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <fbl/auto_call.h> 6#include <fbl/unique_fd.h> 7#include <fbl/unique_ptr.h> 8#include <zircon/device/debug.h> 9#include <zircon/types.h> 10 11#include <dirent.h> 12#include <errno.h> 13#include <fcntl.h> 14#include <stdio.h> 15#include <string.h> 16#include <sys/stat.h> 17#include <unistd.h> 18 19#include "xdc-init.h" 20 21// TODO(jocelyndang): investigate issue with larger buffer sizes. 22static constexpr uint32_t BUFFER_SIZE = 8 * 1024; 23static constexpr uint32_t DEFAULT_STREAM_ID = 1; 24 25typedef struct { 26 off_t file_size; 27} file_header_t; 28 29static void usage(const char* prog_name) { 30 printf("usage:\n"); 31 printf("%s [options]\n", prog_name); 32 printf("\nOptions\n"); 33 printf(" -i <stream id> : ID of stream to transfer over, must be positive. Defaults to 1.\n" 34 " -f <filename> : Name of file to write to or read from.\n" 35 " -d : Download from xdc. This is the default if no mode is specified.\n" 36 " -u : Upload to xdc.\n"); 37} 38 39// Reads the file header from the xdc device and stores it in out_file_header. 40static zx_status_t read_file_header(const fbl::unique_fd& xdc_fd, file_header_t* out_file_header) { 41 unsigned char* buf = reinterpret_cast<unsigned char*>(out_file_header); 42 43 ssize_t res; 44 size_t total_read = 0; 45 size_t len = sizeof(file_header_t); 46 while ((total_read < len) && 47 ((res = read(xdc_fd.get(), buf + total_read, len - total_read)) != 0)) { 48 if (res < 0) { 49 printf("Fatal read error: %s\n", strerror(errno)); 50 return ZX_ERR_IO; 51 } 52 total_read += res; 53 } 54 if (total_read != len) { 55 fprintf(stderr, "Malformed file header, only read %lu bytes, want %lu\n", total_read, len); 56 return ZX_ERR_BAD_STATE; 57 } 58 return ZX_OK; 59} 60 61// Writes the file header to the xdc device and also stores it in out_file_header. 62static zx_status_t write_file_header(const fbl::unique_fd& file_fd, fbl::unique_fd& xdc_fd, 63 file_header_t* out_file_header) { 64 struct stat s; 65 if (fstat(file_fd.get(), &s) < 0) { 66 fprintf(stderr, "could not get size of file, err: %s\n", strerror(errno)); 67 return ZX_ERR_IO; 68 } 69 file_header_t file_header = { .file_size = s.st_size }; 70 unsigned char* buf = reinterpret_cast<unsigned char*>(&file_header); 71 ssize_t res = write(xdc_fd.get(), buf, sizeof(file_header)); 72 if (sizeof(res) != sizeof(file_header)) { 73 fprintf(stderr, "Fatal write err: %s\n", strerror(errno)); 74 return ZX_ERR_IO; 75 } 76 ZX_DEBUG_ASSERT(out_file_header != nullptr); 77 memcpy(out_file_header, &file_header, sizeof(file_header)); 78 return ZX_OK; 79} 80 81// Reads from the src_fd and writes to the dest_fd until src_len bytes has been written, 82// or a fatal error occurs while reading or writing. 83static zx_status_t transfer(fbl::unique_fd& src_fd, off_t src_len, fbl::unique_fd& dest_fd) { 84 printf("Transferring file of size %jd bytes.\n", (uintmax_t)src_len); 85 86 fbl::unique_ptr<unsigned char*[]> buf(new unsigned char*[BUFFER_SIZE]); 87 ssize_t res; 88 off_t total_read = 0; 89 while ((total_read < src_len) && 90 ((res = read(src_fd.get(), buf.get(), BUFFER_SIZE)) != 0)) { 91 if (res < 0) { 92 fprintf(stderr, "Fatal read error: %s\n", strerror(errno)); 93 return ZX_ERR_IO; 94 } 95 total_read += res; 96 97 ssize_t buf_len = res; 98 ssize_t total_written = 0; 99 while (total_written < buf_len) { 100 ssize_t res = write(dest_fd.get(), buf.get() + total_written, buf_len - total_written); 101 if (res < 0) { 102 fprintf(stderr, "Fatal write err: %s\n", strerror(errno)); 103 return ZX_ERR_IO; 104 } 105 total_written += res; 106 } 107 } 108 return ZX_OK; 109} 110 111int main(int argc, char** argv) { 112 auto print_usage = fbl::MakeAutoCall([argv]() { usage(argv[0]); }); 113 114 const char* filename = nullptr; 115 uint32_t stream_id = DEFAULT_STREAM_ID; 116 bool download = true; 117 118 int opt; 119 while ((opt = getopt(argc, argv, "i:f:du")) != -1) { 120 switch (opt) { 121 case 'i': 122 if (sscanf(optarg, "%u", &stream_id) != 1) { 123 fprintf(stderr, "Failed to parse stream id: \"%s\"\n", optarg); 124 return -1; 125 } 126 if (stream_id == 0) { 127 fprintf(stderr, "Stream ID must be positive\n"); 128 return -1; 129 } 130 break; 131 case 'f': 132 filename = optarg; 133 break; 134 case 'd': 135 download = true; 136 break; 137 case 'u': 138 download = false; 139 break; 140 default: 141 fprintf(stderr, "Invalid option\n"); 142 return -1; 143 } 144 } 145 if (!filename) { 146 fprintf(stderr, "No file specified\n"); 147 return -1; 148 } 149 // Finished parsing the arguments without error. 150 print_usage.cancel(); 151 152 fbl::unique_fd xdc_fd; 153 zx_status_t status = configure_xdc(stream_id, xdc_fd); 154 if (status != ZX_OK) { 155 return -1; 156 } 157 158 int file_flags = download ? (O_RDWR | O_CREAT) : O_RDONLY; 159 fbl::unique_fd file_fd(open(filename, file_flags, 0666)); 160 if (!file_fd) { 161 fprintf(stderr, "Failed to open \"%s\", err %s\n", filename, strerror(errno)); 162 return -1; 163 } 164 165 fbl::unique_fd src_fd; 166 fbl::unique_fd dest_fd; 167 168 file_header_t file_header; 169 if (download) { 170 if (read_file_header(xdc_fd, &file_header) != ZX_OK) { 171 return -1; 172 } 173 src_fd = fbl::move(xdc_fd); 174 dest_fd = fbl::move(file_fd); 175 } else { 176 if (write_file_header(file_fd, xdc_fd, &file_header) != ZX_OK) { 177 return -1; 178 } 179 src_fd = fbl::move(file_fd); 180 dest_fd = fbl::move(xdc_fd); 181 } 182 183 status = transfer(src_fd, file_header.file_size, dest_fd); 184 if (status != ZX_OK) { 185 return -1; 186 } 187 return 0; 188} 189