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