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