1 /* 2 * Copyright (c) 2002 Maxim Sobolev 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 #include "stand.h" 29 30 #define NTRIES (3) 31 #define CONF_BUF (512) 32 #define SEEK_BUF (512) 33 34 struct split_file 35 { 36 char **filesv; /* Filenames */ 37 char **descsv; /* Descriptions */ 38 int filesc; /* Number of parts */ 39 int curfile; /* Current file number */ 40 int curfd; /* Current file descriptor */ 41 off_t tot_pos; /* Offset from the beginning of the sequence */ 42 off_t file_pos; /* Offset from the beginning of the slice */ 43 }; 44 45 static int split_openfile(struct split_file *sf); 46 static int splitfs_open(const char *path, struct open_file *f); 47 static int splitfs_close(struct open_file *f); 48 static int splitfs_read(struct open_file *f, void *buf, size_t size, size_t *resid); 49 static off_t splitfs_seek(struct open_file *f, off_t offset, int where); 50 static int splitfs_stat(struct open_file *f, struct stat *sb); 51 52 struct fs_ops splitfs_fsops = { 53 .fs_name = "split", 54 .fo_open = splitfs_open, 55 .fo_close = splitfs_close, 56 .fo_read = splitfs_read, 57 .fo_write = null_write, 58 .fo_seek = splitfs_seek, 59 .fo_stat = splitfs_stat, 60 .fo_readdir = null_readdir, 61 }; 62 63 static void 64 split_file_destroy(struct split_file *sf) 65 { 66 int i; 67 68 if (sf->filesc > 0) { 69 for (i = 0; i < sf->filesc; i++) { 70 free(sf->filesv[i]); 71 free(sf->descsv[i]); 72 } 73 free(sf->filesv); 74 free(sf->descsv); 75 } 76 free(sf); 77 } 78 79 static int 80 split_openfile(struct split_file *sf) 81 { 82 int i; 83 84 for (i = 0;; i++) { 85 sf->curfd = open(sf->filesv[sf->curfile], O_RDONLY); 86 if (sf->curfd >= 0) 87 break; 88 if ((sf->curfd == -1) && (errno != ENOENT)) 89 return (errno); 90 if (i == NTRIES) 91 return (EIO); 92 printf("\nInsert disk labelled %s and press any key...", 93 sf->descsv[sf->curfile]); 94 getchar(); 95 putchar('\n'); 96 } 97 sf->file_pos = 0; 98 return (0); 99 } 100 101 static int 102 splitfs_open(const char *fname, struct open_file *f) 103 { 104 char *buf, *confname, *cp; 105 int conffd; 106 struct split_file *sf; 107 struct stat sb; 108 109 /* Have to be in "just read it" mode */ 110 if (f->f_flags != F_READ) 111 return(EPERM); 112 113 /* If the name already ends in `.split', ignore it */ 114 if ((cp = strrchr(fname, '.')) && (!strcmp(cp, ".split"))) 115 return(ENOENT); 116 117 /* Construct new name */ 118 confname = malloc(strlen(fname) + 7); 119 sprintf(confname, "%s.split", fname); 120 121 /* Try to open the configuration file */ 122 conffd = open(confname, O_RDONLY); 123 free(confname); 124 if (conffd == -1) 125 return(ENOENT); 126 127 if (fstat(conffd, &sb) < 0) { 128 printf("splitfs_open: stat failed\n"); 129 close(conffd); 130 return(ENOENT); 131 } 132 if (!S_ISREG(sb.st_mode)) { 133 printf("splitfs_open: not a file\n"); 134 close(conffd); 135 return(EISDIR); /* best guess */ 136 } 137 138 /* Allocate a split_file structure, populate it from the config file */ 139 sf = malloc(sizeof(struct split_file)); 140 bzero(sf, sizeof(struct split_file)); 141 buf = malloc(CONF_BUF); 142 while (fgetstr(buf, CONF_BUF, conffd) > 0) { 143 cp = buf; 144 while ((*cp != '\0') && (isspace(*cp) == 0)) 145 cp++; 146 if (*cp != '\0') { 147 *cp = '\0'; 148 cp++; 149 } 150 while ((*cp != '\0') && (isspace(*cp) != 0)) 151 cp++; 152 if (*cp == '\0') 153 cp = buf; 154 sf->filesc++; 155 sf->filesv = realloc(sf->filesv, sizeof(*(sf->filesv)) * sf->filesc); 156 sf->descsv = realloc(sf->descsv, sizeof(*(sf->descsv)) * sf->filesc); 157 sf->filesv[sf->filesc - 1] = strdup(buf); 158 sf->descsv[sf->filesc - 1] = strdup(cp); 159 } 160 free(buf); 161 close(conffd); 162 163 if (sf->filesc == 0) { 164 split_file_destroy(sf); 165 return(ENOENT); 166 } 167 errno = split_openfile(sf); 168 if (errno != 0) { 169 split_file_destroy(sf); 170 return(ENOENT); 171 } 172 173 /* Looks OK, we'll take it */ 174 f->f_fsdata = sf; 175 return (0); 176 } 177 178 static int 179 splitfs_close(struct open_file *f) 180 { 181 int fd; 182 struct split_file *sf; 183 184 sf = (struct split_file *)f->f_fsdata; 185 fd = sf->curfd; 186 split_file_destroy(sf); 187 return(close(fd)); 188 } 189 190 static int 191 splitfs_read(struct open_file *f, void *buf, size_t size, size_t *resid) 192 { 193 ssize_t nread; 194 size_t totread; 195 struct split_file *sf; 196 197 sf = (struct split_file *)f->f_fsdata; 198 totread = 0; 199 do { 200 nread = read(sf->curfd, buf, size - totread); 201 202 /* Error? */ 203 if (nread == -1) 204 return (errno); 205 206 sf->tot_pos += nread; 207 sf->file_pos += nread; 208 totread += nread; 209 buf = (char *)buf + nread; 210 211 if (totread < size) { /* EOF */ 212 if (sf->curfile == (sf->filesc - 1)) /* Last slice */ 213 break; 214 215 /* Close previous slice */ 216 if (close(sf->curfd) != 0) 217 return (errno); 218 219 sf->curfile++; 220 errno = split_openfile(sf); 221 if (errno) 222 return (errno); 223 } 224 } while (totread < size); 225 226 if (resid != NULL) 227 *resid = size - totread; 228 229 return (0); 230 } 231 232 static off_t 233 splitfs_seek(struct open_file *f, off_t offset, int where) 234 { 235 int nread; 236 size_t resid; 237 off_t new_pos, seek_by; 238 struct split_file *sf; 239 240 sf = (struct split_file *)f->f_fsdata; 241 242 seek_by = offset; 243 switch (where) { 244 case SEEK_SET: 245 seek_by -= sf->tot_pos; 246 break; 247 case SEEK_CUR: 248 break; 249 case SEEK_END: 250 panic("splitfs_seek: SEEK_END not supported"); 251 break; 252 default: 253 errno = EINVAL; 254 return (-1); 255 } 256 257 if (seek_by > 0) { 258 /* 259 * Seek forward - implemented using splitfs_read(), because otherwise we'll be 260 * unable to detect that we have crossed slice boundary and hence 261 * unable to do a long seek crossing that boundary. 262 */ 263 void *tmp; 264 265 tmp = malloc(SEEK_BUF); 266 if (tmp == NULL) { 267 errno = ENOMEM; 268 return (-1); 269 } 270 271 nread = 0; 272 for (; seek_by > 0; seek_by -= nread) { 273 resid = 0; 274 errno = splitfs_read(f, tmp, min(seek_by, SEEK_BUF), &resid); 275 nread = min(seek_by, SEEK_BUF) - resid; 276 if ((errno != 0) || (nread == 0)) 277 /* Error or EOF */ 278 break; 279 } 280 free(tmp); 281 if (errno != 0) 282 return (-1); 283 } 284 285 if (seek_by != 0) { 286 /* Seek backward or seek past the boundary of the last slice */ 287 if (sf->file_pos + seek_by < 0) 288 panic("splitfs_seek: can't seek past the beginning of the slice"); 289 new_pos = lseek(sf->curfd, seek_by, SEEK_CUR); 290 if (new_pos < 0) { 291 errno = EINVAL; 292 return (-1); 293 } 294 sf->tot_pos += new_pos - sf->file_pos; 295 sf->file_pos = new_pos; 296 } 297 298 return (sf->tot_pos); 299 } 300 301 static int 302 splitfs_stat(struct open_file *f, struct stat *sb) 303 { 304 int result; 305 struct split_file *sf = (struct split_file *)f->f_fsdata; 306 307 /* stat as normal, but indicate that size is unknown */ 308 if ((result = fstat(sf->curfd, sb)) == 0) 309 sb->st_size = -1; 310 return (result); 311 } 312