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