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