splitfs.c revision 92494
1337414Skevans/* 2337414Skevans * Copyright (c) 2002 Maxim Sobolev 3336668Skevans * All rights reserved. 4336668Skevans * 5336668Skevans * Redistribution and use in source and binary forms, with or without 6336668Skevans * modification, are permitted provided that the following conditions 7336668Skevans * are met: 8336668Skevans * 1. Redistributions of source code must retain the above copyright 9336668Skevans * notice, this list of conditions and the following disclaimer. 10336668Skevans * 2. Redistributions in binary form must reproduce the above copyright 11336668Skevans * notice, this list of conditions and the following disclaimer in the 12336668Skevans * documentation and/or other materials provided with the distribution. 13336668Skevans * 14336668Skevans * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15336668Skevans * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16336668Skevans * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17336668Skevans * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18336668Skevans * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19336668Skevans * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20336668Skevans * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21336668Skevans * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22336668Skevans * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23336668Skevans * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24336668Skevans * SUCH DAMAGE. 25336668Skevans */ 26336668Skevans 27336668Skevans#include <sys/cdefs.h> 28337416Skevans__FBSDID("$FreeBSD: head/lib/libstand/splitfs.c 92494 2002-03-17 12:18:05Z sobomax $"); 29337416Skevans 30337416Skevans#include "stand.h" 31346429Skevans 32346429Skevans#define NTRIES (3) 33336668Skevans#define CONF_BUF (512) 34346429Skevans#define SEEK_BUF (512) 35356593Skevans 36355662Skevansstruct split_file 37355662Skevans{ 38355662Skevans char **filesv; /* Filenames */ 39336668Skevans char **descsv; /* Descriptions */ 40336668Skevans int filesc; /* Number of parts */ 41336668Skevans int curfile; /* Current file number */ 42336668Skevans int curfd; /* Current file descriptor */ 43336668Skevans off_t tot_pos; /* Offset from the beginning of the sequence */ 44336668Skevans off_t file_pos; /* Offset from the beginning of the slice */ 45336668Skevans}; 46336668Skevans 47336668Skevansstatic int splitfs_open(const char *path, struct open_file *f); 48336668Skevansstatic int splitfs_close(struct open_file *f); 49336668Skevansstatic int splitfs_read(struct open_file *f, void *buf, size_t size, size_t *resid); 50356593Skevansstatic off_t splitfs_seek(struct open_file *f, off_t offset, int where); 51356593Skevansstatic int splitfs_stat(struct open_file *f, struct stat *sb); 52356593Skevans 53356593Skevansstruct fs_ops splitfs_fsops = { 54356593Skevans "split", 55346429Skevans splitfs_open, 56356593Skevans splitfs_close, 57356593Skevans splitfs_read, 58356593Skevans null_write, 59356593Skevans splitfs_seek, 60346429Skevans splitfs_stat, 61346429Skevans null_readdir 62337596Skevans}; 63337592Skevans 64337592Skevansstatic void 65337592Skevanssplit_file_destroy(struct split_file *sf) 66337596Skevans{ 67337592Skevans int i; 68346429Skevans 69346429Skevans if (sf->filesc > 0) { 70346429Skevans for (i = 0; i < sf->filesc; i++) { 71336668Skevans free(sf->filesv[i]); 72336699Skevans free(sf->descsv[i]); 73336699Skevans } 74336699Skevans free(sf->filesv); 75336699Skevans free(sf->descsv); 76346429Skevans } 77336699Skevans free(sf); 78346429Skevans} 79355662Skevans 80346429Skevansstatic int 81336699Skevanssplitfs_open(const char *fname, struct open_file *f) 82346429Skevans{ 83346429Skevans char *buf, *confname, *cp; 84346429Skevans int conffd; 85346429Skevans struct split_file *sf; 86346429Skevans struct stat sb; 87346429Skevans 88346429Skevans printf("%s\n", fname); 89346429Skevans /* Have to be in "just read it" mode */ 90346429Skevans if (f->f_flags != F_READ) 91346429Skevans return(EPERM); 92346429Skevans 93336699Skevans /* If the name already ends in `.split', ignore it */ 94346429Skevans if ((cp = strrchr(fname, '.')) && (!strcmp(cp, ".split"))) 95346429Skevans return(ENOENT); 96336699Skevans 97336699Skevans /* Construct new name */ 98346429Skevans confname = malloc(strlen(fname) + 7); 99346429Skevans sprintf(confname, "%s.split", fname); 100336699Skevans 101336699Skevans /* Try to open the configuration file */ 102336699Skevans conffd = open(confname, O_RDONLY); 103336699Skevans free(confname); 104336668Skevans if (conffd == -1) 105336668Skevans return(ENOENT); 106336668Skevans 107336668Skevans if (fstat(conffd, &sb) < 0) { 108346429Skevans printf("splitfs_open: stat failed\n"); 109336668Skevans close(conffd); 110346429Skevans return(ENOENT); 111336668Skevans } 112336696Skevans if (!S_ISREG(sb.st_mode)) { 113336696Skevans printf("splitfs_open: not a file\n"); 114336668Skevans close(conffd); 115336696Skevans return(EISDIR); /* best guess */ 116336696Skevans } 117336699Skevans 118336696Skevans /* Allocate a split_file structure, populate it from the config file */ 119336696Skevans sf = malloc(sizeof(struct split_file)); 120336668Skevans bzero(sf, sizeof(struct split_file)); 121336696Skevans buf = malloc(CONF_BUF); 122336696Skevans while (fgetstr(buf, CONF_BUF, conffd) > 0) { 123336668Skevans cp = buf; 124346429Skevans while ((*cp != '\0') && (isspace(*cp) == 0)) 125346429Skevans cp++; 126346429Skevans if (*cp != '\0') { 127346429Skevans *cp = '\0'; 128346429Skevans cp++; 129346429Skevans } 130346429Skevans while ((*cp != '\0') && (isspace(*cp) != 0)) 131346429Skevans cp++; 132346429Skevans if (*cp == '\0') 133346429Skevans cp = buf; 134346429Skevans sf->filesc++; 135346429Skevans sf->filesv = realloc(sf->filesv, sizeof(*(sf->filesv)) * sf->filesc); 136346429Skevans sf->descsv = realloc(sf->descsv, sizeof(*(sf->descsv)) * sf->filesc); 137346429Skevans sf->filesv[sf->filesc - 1] = strdup(buf); 138346429Skevans sf->descsv[sf->filesc - 1] = strdup(cp); 139346429Skevans } 140346429Skevans free(buf); 141346429Skevans close(conffd); 142336668Skevans 143336696Skevans if ((sf->filesc == 0) || ((sf->curfd = open(sf->filesv[0], O_RDONLY)) == -1)) { 144336696Skevans split_file_destroy(sf); 145336668Skevans return(ENOENT); 146336696Skevans } 147336696Skevans 148336696Skevans /* Looks OK, we'll take it */ 149336696Skevans f->f_fsdata = sf; 150336696Skevans return (0); 151346429Skevans} 152336696Skevans 153336696Skevansstatic int 154346429Skevanssplitfs_close(struct open_file *f) 155346429Skevans{ 156336696Skevans int fd; 157336696Skevans struct split_file *sf; 158346429Skevans 159336696Skevans sf = (struct split_file *)f->f_fsdata; 160336696Skevans fd = sf->curfd; 161346429Skevans split_file_destroy(sf); 162346429Skevans return(close(fd)); 163346429Skevans} 164346429Skevans 165336696Skevansstatic int 166336668Skevanssplitfs_read(struct open_file *f, void *buf, size_t size, size_t *resid) 167336696Skevans{ 168336696Skevans int i, nread, totread; 169336696Skevans struct split_file *sf; 170336696Skevans 171336696Skevans sf = (struct split_file *)f->f_fsdata; 172336696Skevans totread = 0; 173336696Skevans do { 174336696Skevans nread = read(sf->curfd, buf, size - totread); 175336696Skevans 176336696Skevans /* Error? */ 177336668Skevans if (nread == -1) 178336668Skevans return (errno); 179336668Skevans 180336668Skevans sf->tot_pos += nread; 181336668Skevans sf->file_pos += nread; 182336668Skevans totread += nread; 183336668Skevans buf += nread; 184336668Skevans 185336668Skevans if (totread < size) { /* EOF */ 186336701Skevans if (sf->curfile == (sf->filesc - 1)) /* Last slice */ 187336696Skevans break; 188336696Skevans 189336668Skevans /* Close previous slice */ 190336668Skevans if (close(sf->curfd) != 0) 191336668Skevans return (errno); 192336668Skevans 193337343Skevans sf->curfile++; 194337343Skevans for (i = 0;; i++) { 195337343Skevans sf->curfd = open(sf->filesv[sf->curfile], O_RDONLY); 196337343Skevans if (sf->curfd >= 0) 197337343Skevans break; 198337343Skevans if ((sf->curfd == -1) && (errno != ENOENT)) 199336668Skevans return (errno); 200337343Skevans if (i == NTRIES) 201337343Skevans return (EIO); 202337343Skevans printf("\nInsert disk labelled %s and press any key...", sf->descsv[sf->curfile]); 203356593Skevans getchar();putchar('\n'); 204356593Skevans } 205356593Skevans sf->file_pos = 0; 206356593Skevans } 207356593Skevans } while (totread < size); 208356593Skevans 209356593Skevans if (resid != NULL) 210356593Skevans *resid = size - totread; 211356593Skevans 212356593Skevans return (0); 213356593Skevans} 214356593Skevans 215356593Skevansstatic off_t 216356593Skevanssplitfs_seek(struct open_file *f, off_t offset, int where) 217356593Skevans{ 218356593Skevans int nread; 219356593Skevans size_t resid; 220356593Skevans off_t new_pos, seek_by; 221356593Skevans struct split_file *sf; 222356593Skevans 223356593Skevans sf = (struct split_file *)f->f_fsdata; 224356593Skevans 225356593Skevans seek_by = offset; 226356593Skevans switch (where) { 227356593Skevans case SEEK_SET: 228356593Skevans seek_by -= sf->tot_pos; 229356593Skevans break; 230356593Skevans case SEEK_CUR: 231337409Skevans break; 232356593Skevans case SEEK_END: 233356593Skevans panic("splitfs_seek: SEEK_END not supported"); 234356593Skevans break; 235356593Skevans } 236356593Skevans 237356593Skevans if (seek_by > 0) { 238356593Skevans /* 239356593Skevans * Seek forward - implemented using splitfs_read(), because otherwise we'll be 240356593Skevans * unable to detect that we have crossed slice boundary and hence 241356593Skevans * unable to do a long seek crossing that boundary. 242356593Skevans */ 243356593Skevans void *tmp; 244356593Skevans 245356593Skevans tmp = malloc(SEEK_BUF); 246356593Skevans if (tmp == NULL) 247356593Skevans return (-1); 248356593Skevans 249356593Skevans nread = 0; 250356593Skevans for (; seek_by > 0; seek_by -= nread) { 251356593Skevans resid = 0; 252356593Skevans errno = splitfs_read(f, tmp, min(seek_by, SEEK_BUF), &resid); 253356593Skevans nread = min(seek_by, SEEK_BUF) - resid; 254356593Skevans if ((errno != 0) || (nread == 0)) 255356593Skevans /* Error or EOF */ 256356593Skevans break; 257356593Skevans } 258356593Skevans free(tmp); 259356593Skevans if (errno != 0) 260356593Skevans return (-1); 261356593Skevans } 262356593Skevans 263356593Skevans if (seek_by != 0) { 264356593Skevans /* Seek backward or seek past the boundary of the last slice */ 265356593Skevans if (sf->file_pos + seek_by < 0) 266356593Skevans panic("splitfs_seek: can't seek past the beginning of the slice"); 267356593Skevans new_pos = lseek(sf->curfd, seek_by, SEEK_CUR); 268356593Skevans if (new_pos < 0) 269356593Skevans return (-1); 270356593Skevans sf->tot_pos += new_pos - sf->file_pos; 271356593Skevans sf->file_pos = new_pos; 272356593Skevans } 273356593Skevans 274356593Skevans return (sf->tot_pos); 275356593Skevans} 276356593Skevans 277356593Skevansstatic int 278356593Skevanssplitfs_stat(struct open_file *f, struct stat *sb) 279356593Skevans{ 280356593Skevans int result; 281356593Skevans struct split_file *sf = (struct split_file *)f->f_fsdata; 282356593Skevans 283356593Skevans /* stat as normal, but indicate that size is unknown */ 284356593Skevans if ((result = fstat(sf->curfd, sb)) == 0) 285356593Skevans sb->st_size = -1; 286356593Skevans return (result); 287356593Skevans} 288356593Skevans