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 25netfile_state netfile = { 26 .fd = -1, 27 .needs_rename = false, 28}; 29 30static 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 54int 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; 64again: // 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; 109err: 110 netfile.filename[0] = '\0'; 111 return -errno; 112} 113 114int 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 128int 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 145int 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 159int 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 176int 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. 199void 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