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