1 // Copyright 2016 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 "netsvc.h"
6
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <sys/stat.h>
14
15 #include <inet6/inet6.h>
16 #include <inet6/netifc.h>
17
18 #include <zircon/processargs.h>
19 #include <zircon/syscalls.h>
20
21 #include <zircon/boot/netboot.h>
22
23 #define TMP_SUFFIX ".netsvc.tmp"
24
25 netfile_state netfile = {
26 .fd = -1,
27 .needs_rename = false,
28 };
29
netfile_mkdir(const char * filename)30 static int netfile_mkdir(const char* filename) {
31 const char* ptr = filename[0] == '/' ? filename + 1 : filename;
32 struct stat st;
33 char tmp[1024];
34 for (;;) {
35 ptr = strchr(ptr, '/');
36 if (!ptr) {
37 return 0;
38 }
39 memcpy(tmp, filename, ptr - filename);
40 tmp[ptr - filename] = '\0';
41 ptr += 1;
42 if (stat(tmp, &st) < 0) {
43 if (errno == ENOENT) {
44 if (mkdir(tmp, 0755) < 0) {
45 return -1;
46 }
47 } else {
48 return -1;
49 }
50 }
51 }
52 }
53
netfile_open(const char * filename,uint32_t arg,size_t * file_size)54 int netfile_open(const char *filename, uint32_t arg, size_t* file_size) {
55 if (netfile.fd >= 0) {
56 printf("netsvc: closing still-open '%s', replacing with '%s'\n", netfile.filename, filename);
57 close(netfile.fd);
58 netfile.fd = -1;
59 }
60 size_t len = strlen(filename);
61 strlcpy(netfile.filename, filename, sizeof(netfile.filename));
62
63 struct stat st;
64 again: // label here to catch filename=/path/to/new/directory/
65 if (stat(filename, &st) == 0 && S_ISDIR(st.st_mode)) {
66 errno = EISDIR;
67 goto err;
68 }
69
70 switch (arg) {
71 case O_RDONLY:
72 netfile.needs_rename = false;
73 netfile.fd = open(filename, O_RDONLY);
74 if (file_size) {
75 *file_size = st.st_size;
76 }
77 break;
78 case O_WRONLY: {
79 // If we're writing a file, actually write to "filename + TMP_SUFFIX",
80 // and rename to the final destination when we would close. This makes
81 // written files appear to atomically update.
82 if (len + strlen(TMP_SUFFIX) + 1 > PATH_MAX) {
83 errno = ENAMETOOLONG;
84 goto err;
85 }
86 strcat(netfile.filename, TMP_SUFFIX);
87 netfile.needs_rename = true;
88 netfile.fd = open(netfile.filename, O_WRONLY|O_CREAT|O_TRUNC);
89 netfile.filename[len] = '\0';
90 if (netfile.fd < 0 && errno == ENOENT) {
91 if (netfile_mkdir(filename) == 0) {
92 goto again;
93 }
94 }
95 break;
96 }
97 default:
98 printf("netsvc: open '%s' with invalid mode %d\n", filename, arg);
99 errno = EINVAL;
100 }
101 if (netfile.fd < 0) {
102 goto err;
103 } else {
104 strlcpy(netfile.filename, filename, sizeof(netfile.filename));
105 netfile.offset = 0;
106 }
107
108 return 0;
109 err:
110 netfile.filename[0] = '\0';
111 return -errno;
112 }
113
netfile_offset_read(void * data_out,off_t offset,size_t max_len)114 int netfile_offset_read(void* data_out, off_t offset, size_t max_len) {
115 if (netfile.fd < 0) {
116 printf("netsvc: read, but no open file\n");
117 return -EBADF;
118 }
119 if (offset != netfile.offset) {
120 if (lseek(netfile.fd, offset, SEEK_SET) != offset) {
121 return -errno;
122 }
123 netfile.offset = offset;
124 }
125 return netfile_read(data_out, max_len);
126 }
127
netfile_read(void * data_out,size_t data_sz)128 int netfile_read(void *data_out, size_t data_sz) {
129 if (netfile.fd < 0) {
130 printf("netsvc: read, but no open file\n");
131 return -EBADF;
132 }
133 ssize_t n = read(netfile.fd, data_out, data_sz);
134 if (n < 0) {
135 printf("netsvc: error reading '%s': %d\n", netfile.filename, errno);
136 int result = (errno == 0) ? -EIO : -errno;
137 close(netfile.fd);
138 netfile.fd = -1;
139 return result;
140 }
141 netfile.offset += n;
142 return n;
143 }
144
netfile_offset_write(const char * data,off_t offset,size_t length)145 int netfile_offset_write(const char* data, off_t offset, size_t length) {
146 if (netfile.fd < 0) {
147 printf("netsvc: write, but no open file\n");
148 return -EBADF;
149 }
150 if (offset != netfile.offset) {
151 if (lseek(netfile.fd, offset, SEEK_SET) != offset) {
152 return -errno;
153 }
154 netfile.offset = offset;
155 }
156 return netfile_write(data, length);
157 }
158
netfile_write(const char * data,size_t len)159 int netfile_write(const char* data, size_t len) {
160 if (netfile.fd < 0) {
161 printf("netsvc: write, but no open file\n");
162 return -EBADF;
163 }
164 ssize_t n = write(netfile.fd, data, len);
165 if (n != (ssize_t)len) {
166 printf("netsvc: error writing %s: %d\n", netfile.filename, errno);
167 int result = (errno == 0) ? -EIO : -errno;
168 close(netfile.fd);
169 netfile.fd = -1;
170 return result;
171 }
172 netfile.offset += len;
173 return len;
174 }
175
netfile_close(void)176 int netfile_close(void) {
177 int result = 0;
178 if (netfile.fd < 0) {
179 printf("netsvc: close, but no open file\n");
180 } else {
181 if (netfile.needs_rename) {
182 char src[PATH_MAX];
183 strlcpy(src, netfile.filename, sizeof(src));
184 strlcat(src, TMP_SUFFIX, sizeof(src));
185 if (rename(src, netfile.filename)) {
186 printf("netsvc: failed to rename temporary file: %s\n", strerror(errno));
187 }
188 }
189 if (close(netfile.fd)) {
190 result = (errno == 0) ? -EIO : -errno;
191 }
192 netfile.fd = -1;
193 }
194 return result;
195 }
196
197 // Clean up if we abort before finishing a write. Close out and unlink it, rather than
198 // leaving an incomplete file.
netfile_abort_write(void)199 void netfile_abort_write(void) {
200 if (netfile.fd < 0) {
201 return;
202 }
203 close(netfile.fd);
204 netfile.fd = -1;
205 char tmp[PATH_MAX];
206 const char* filename;
207 if (netfile.needs_rename) {
208 strlcpy(tmp, netfile.filename, sizeof(tmp));
209 strlcat(tmp, TMP_SUFFIX, sizeof(tmp));
210 filename = tmp;
211 } else {
212 filename = netfile.filename;
213 }
214 if (unlink(filename) != 0) {
215 printf("netsvc: failed to unlink aborted file %s\n", filename);
216 }
217 }
218