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 "stand.h" 28 29 #define NTRIES (3) 30 #define CONF_BUF (512) 31 #define SEEK_BUF (512) 32 33 struct split_file 34 { 35 char **filesv; /* Filenames */ 36 char **descsv; /* Descriptions */ 37 int filesc; /* Number of parts */ 38 int curfile; /* Current file number */ 39 int curfd; /* Current file descriptor */ 40 off_t tot_pos; /* Offset from the beginning of the sequence */ 41 off_t file_pos; /* Offset from the beginning of the slice */ 42 }; 43 44 static int split_openfile(struct split_file *sf); 45 static int splitfs_open(const char *path, struct open_file *f); 46 static int splitfs_close(struct open_file *f); 47 static int splitfs_read(struct open_file *f, void *buf, size_t size, size_t *resid); 48 static off_t splitfs_seek(struct open_file *f, off_t offset, int where); 49 static int splitfs_stat(struct open_file *f, struct stat *sb); 50 51 struct fs_ops splitfs_fsops = { 52 .fs_name = "split", 53 .fo_open = splitfs_open, 54 .fo_close = splitfs_close, 55 .fo_read = splitfs_read, 56 .fo_write = null_write, 57 .fo_seek = splitfs_seek, 58 .fo_stat = splitfs_stat, 59 .fo_readdir = null_readdir, 60 }; 61 62 static void 63 split_file_destroy(struct split_file *sf) 64 { 65 int i; 66 67 if (sf->filesc > 0) { 68 for (i = 0; i < sf->filesc; i++) { 69 free(sf->filesv[i]); 70 free(sf->descsv[i]); 71 } 72 free(sf->filesv); 73 free(sf->descsv); 74 } 75 free(sf); 76 } 77 78 static int 79 split_openfile(struct split_file *sf) 80 { 81 int i; 82 83 for (i = 0;; i++) { 84 sf->curfd = open(sf->filesv[sf->curfile], O_RDONLY); 85 if (sf->curfd >= 0) 86 break; 87 if ((sf->curfd == -1) && (errno != ENOENT)) 88 return (errno); 89 if (i == NTRIES) 90 return (EIO); 91 printf("\nInsert disk labelled %s and press any key...", 92 sf->descsv[sf->curfile]); 93 getchar(); 94 putchar('\n'); 95 } 96 sf->file_pos = 0; 97 return (0); 98 } 99 100 static int 101 splitfs_open(const char *fname, struct open_file *f) 102 { 103 char *buf, *confname, *cp; 104 int conffd; 105 struct split_file *sf; 106 struct stat sb; 107 108 /* Have to be in "just read it" mode */ 109 if (f->f_flags != F_READ) 110 return(EPERM); 111 112 /* If the name already ends in `.split', ignore it */ 113 if ((cp = strrchr(fname, '.')) && (!strcmp(cp, ".split"))) 114 return(ENOENT); 115 116 /* Construct new name */ 117 confname = malloc(strlen(fname) + 7); 118 sprintf(confname, "%s.split", fname); 119 120 /* Try to open the configuration file */ 121 conffd = open(confname, O_RDONLY); 122 free(confname); 123 if (conffd == -1) 124 return(ENOENT); 125 126 if (fstat(conffd, &sb) < 0) { 127 printf("splitfs_open: stat failed\n"); 128 close(conffd); 129 return(ENOENT); 130 } 131 if (!S_ISREG(sb.st_mode)) { 132 printf("splitfs_open: not a file\n"); 133 close(conffd); 134 return(EISDIR); /* best guess */ 135 } 136 137 /* Allocate a split_file structure, populate it from the config file */ 138 sf = malloc(sizeof(struct split_file)); 139 bzero(sf, sizeof(struct split_file)); 140 buf = malloc(CONF_BUF); 141 while (fgetstr(buf, CONF_BUF, conffd) > 0) { 142 cp = buf; 143 while ((*cp != '\0') && (isspace(*cp) == 0)) 144 cp++; 145 if (*cp != '\0') { 146 *cp = '\0'; 147 cp++; 148 } 149 while ((*cp != '\0') && (isspace(*cp) != 0)) 150 cp++; 151 if (*cp == '\0') 152 cp = buf; 153 sf->filesc++; 154 sf->filesv = realloc(sf->filesv, sizeof(*(sf->filesv)) * sf->filesc); 155 sf->descsv = realloc(sf->descsv, sizeof(*(sf->descsv)) * sf->filesc); 156 sf->filesv[sf->filesc - 1] = strdup(buf); 157 sf->descsv[sf->filesc - 1] = strdup(cp); 158 } 159 free(buf); 160 close(conffd); 161 162 if (sf->filesc == 0) { 163 split_file_destroy(sf); 164 return(ENOENT); 165 } 166 errno = split_openfile(sf); 167 if (errno != 0) { 168 split_file_destroy(sf); 169 return(ENOENT); 170 } 171 172 /* Looks OK, we'll take it */ 173 f->f_fsdata = sf; 174 return (0); 175 } 176 177 static int 178 splitfs_close(struct open_file *f) 179 { 180 int fd; 181 struct split_file *sf; 182 183 sf = (struct split_file *)f->f_fsdata; 184 fd = sf->curfd; 185 split_file_destroy(sf); 186 return(close(fd)); 187 } 188 189 static int 190 splitfs_read(struct open_file *f, void *buf, size_t size, size_t *resid) 191 { 192 ssize_t nread; 193 size_t totread; 194 struct split_file *sf; 195 196 sf = (struct split_file *)f->f_fsdata; 197 totread = 0; 198 do { 199 nread = read(sf->curfd, buf, size - totread); 200 201 /* Error? */ 202 if (nread == -1) 203 return (errno); 204 205 sf->tot_pos += nread; 206 sf->file_pos += nread; 207 totread += nread; 208 buf = (char *)buf + nread; 209 210 if (totread < size) { /* EOF */ 211 if (sf->curfile == (sf->filesc - 1)) /* Last slice */ 212 break; 213 214 /* Close previous slice */ 215 if (close(sf->curfd) != 0) 216 return (errno); 217 218 sf->curfile++; 219 errno = split_openfile(sf); 220 if (errno) 221 return (errno); 222 } 223 } while (totread < size); 224 225 if (resid != NULL) 226 *resid = size - totread; 227 228 return (0); 229 } 230 231 static off_t 232 splitfs_seek(struct open_file *f, off_t offset, int where) 233 { 234 int nread; 235 size_t resid; 236 off_t new_pos, seek_by; 237 struct split_file *sf; 238 239 sf = (struct split_file *)f->f_fsdata; 240 241 seek_by = offset; 242 switch (where) { 243 case SEEK_SET: 244 seek_by -= sf->tot_pos; 245 break; 246 case SEEK_CUR: 247 break; 248 case SEEK_END: 249 panic("splitfs_seek: SEEK_END not supported"); 250 break; 251 default: 252 errno = EINVAL; 253 return (-1); 254 } 255 256 if (seek_by > 0) { 257 /* 258 * Seek forward - implemented using splitfs_read(), because otherwise we'll be 259 * unable to detect that we have crossed slice boundary and hence 260 * unable to do a long seek crossing that boundary. 261 */ 262 void *tmp; 263 264 tmp = malloc(SEEK_BUF); 265 if (tmp == NULL) { 266 errno = ENOMEM; 267 return (-1); 268 } 269 270 nread = 0; 271 for (; seek_by > 0; seek_by -= nread) { 272 resid = 0; 273 errno = splitfs_read(f, tmp, min(seek_by, SEEK_BUF), &resid); 274 nread = min(seek_by, SEEK_BUF) - resid; 275 if ((errno != 0) || (nread == 0)) 276 /* Error or EOF */ 277 break; 278 } 279 free(tmp); 280 if (errno != 0) 281 return (-1); 282 } 283 284 if (seek_by != 0) { 285 /* Seek backward or seek past the boundary of the last slice */ 286 if (sf->file_pos + seek_by < 0) 287 panic("splitfs_seek: can't seek past the beginning of the slice"); 288 new_pos = lseek(sf->curfd, seek_by, SEEK_CUR); 289 if (new_pos < 0) { 290 errno = EINVAL; 291 return (-1); 292 } 293 sf->tot_pos += new_pos - sf->file_pos; 294 sf->file_pos = new_pos; 295 } 296 297 return (sf->tot_pos); 298 } 299 300 static int 301 splitfs_stat(struct open_file *f, struct stat *sb) 302 { 303 int result; 304 struct split_file *sf = (struct split_file *)f->f_fsdata; 305 306 /* stat as normal, but indicate that size is unknown */ 307 if ((result = fstat(sf->curfd, sb)) == 0) 308 sb->st_size = -1; 309 return (result); 310 } 311