splitfs.c revision 124571
192494Ssobomax/* 292494Ssobomax * Copyright (c) 2002 Maxim Sobolev 392494Ssobomax * All rights reserved. 492494Ssobomax * 592494Ssobomax * Redistribution and use in source and binary forms, with or without 692494Ssobomax * modification, are permitted provided that the following conditions 792494Ssobomax * are met: 892494Ssobomax * 1. Redistributions of source code must retain the above copyright 992494Ssobomax * notice, this list of conditions and the following disclaimer. 1092494Ssobomax * 2. Redistributions in binary form must reproduce the above copyright 1192494Ssobomax * notice, this list of conditions and the following disclaimer in the 1292494Ssobomax * documentation and/or other materials provided with the distribution. 1392494Ssobomax * 1492494Ssobomax * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1592494Ssobomax * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1692494Ssobomax * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1792494Ssobomax * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1892494Ssobomax * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1992494Ssobomax * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2092494Ssobomax * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2192494Ssobomax * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2292494Ssobomax * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2392494Ssobomax * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2492494Ssobomax * SUCH DAMAGE. 2592494Ssobomax */ 2692494Ssobomax 2792494Ssobomax#include <sys/cdefs.h> 2892494Ssobomax__FBSDID("$FreeBSD: head/lib/libstand/splitfs.c 124571 2004-01-15 18:36:48Z jhb $"); 2992494Ssobomax 3092494Ssobomax#include "stand.h" 3192494Ssobomax 3292494Ssobomax#define NTRIES (3) 3392494Ssobomax#define CONF_BUF (512) 3492494Ssobomax#define SEEK_BUF (512) 3592494Ssobomax 3692494Ssobomaxstruct split_file 3792494Ssobomax{ 3892494Ssobomax char **filesv; /* Filenames */ 3992494Ssobomax char **descsv; /* Descriptions */ 4092494Ssobomax int filesc; /* Number of parts */ 4192494Ssobomax int curfile; /* Current file number */ 4292494Ssobomax int curfd; /* Current file descriptor */ 4392494Ssobomax off_t tot_pos; /* Offset from the beginning of the sequence */ 4492494Ssobomax off_t file_pos; /* Offset from the beginning of the slice */ 4592494Ssobomax}; 4692494Ssobomax 4792494Ssobomaxstatic int splitfs_open(const char *path, struct open_file *f); 4892494Ssobomaxstatic int splitfs_close(struct open_file *f); 4992494Ssobomaxstatic int splitfs_read(struct open_file *f, void *buf, size_t size, size_t *resid); 5092494Ssobomaxstatic off_t splitfs_seek(struct open_file *f, off_t offset, int where); 5192494Ssobomaxstatic int splitfs_stat(struct open_file *f, struct stat *sb); 5292494Ssobomax 5392494Ssobomaxstruct fs_ops splitfs_fsops = { 5492494Ssobomax "split", 5592494Ssobomax splitfs_open, 5692494Ssobomax splitfs_close, 5792494Ssobomax splitfs_read, 5892494Ssobomax null_write, 5992494Ssobomax splitfs_seek, 6092494Ssobomax splitfs_stat, 6192494Ssobomax null_readdir 6292494Ssobomax}; 6392494Ssobomax 6492494Ssobomaxstatic void 6592494Ssobomaxsplit_file_destroy(struct split_file *sf) 6692494Ssobomax{ 67124571Sjhb int i; 6892494Ssobomax 69124571Sjhb if (sf->filesc > 0) { 7092494Ssobomax for (i = 0; i < sf->filesc; i++) { 7192494Ssobomax free(sf->filesv[i]); 7292494Ssobomax free(sf->descsv[i]); 7392494Ssobomax } 7492494Ssobomax free(sf->filesv); 7592494Ssobomax free(sf->descsv); 76124571Sjhb } 77124571Sjhb free(sf); 7892494Ssobomax} 7992494Ssobomax 8092494Ssobomaxstatic int 8192494Ssobomaxsplitfs_open(const char *fname, struct open_file *f) 8292494Ssobomax{ 8392494Ssobomax char *buf, *confname, *cp; 8492494Ssobomax int conffd; 8592494Ssobomax struct split_file *sf; 8692494Ssobomax struct stat sb; 8792494Ssobomax 8892494Ssobomax /* Have to be in "just read it" mode */ 8992494Ssobomax if (f->f_flags != F_READ) 9092494Ssobomax return(EPERM); 9192494Ssobomax 9292494Ssobomax /* If the name already ends in `.split', ignore it */ 9392494Ssobomax if ((cp = strrchr(fname, '.')) && (!strcmp(cp, ".split"))) 9492494Ssobomax return(ENOENT); 9592494Ssobomax 9692494Ssobomax /* Construct new name */ 9792494Ssobomax confname = malloc(strlen(fname) + 7); 9892494Ssobomax sprintf(confname, "%s.split", fname); 9992494Ssobomax 10092494Ssobomax /* Try to open the configuration file */ 10192494Ssobomax conffd = open(confname, O_RDONLY); 10292494Ssobomax free(confname); 10392494Ssobomax if (conffd == -1) 10492494Ssobomax return(ENOENT); 10592494Ssobomax 10692494Ssobomax if (fstat(conffd, &sb) < 0) { 10792494Ssobomax printf("splitfs_open: stat failed\n"); 10892494Ssobomax close(conffd); 10992494Ssobomax return(ENOENT); 11092494Ssobomax } 11192494Ssobomax if (!S_ISREG(sb.st_mode)) { 11292494Ssobomax printf("splitfs_open: not a file\n"); 11392494Ssobomax close(conffd); 11492494Ssobomax return(EISDIR); /* best guess */ 11592494Ssobomax } 11692494Ssobomax 11792494Ssobomax /* Allocate a split_file structure, populate it from the config file */ 11892494Ssobomax sf = malloc(sizeof(struct split_file)); 11992494Ssobomax bzero(sf, sizeof(struct split_file)); 12092494Ssobomax buf = malloc(CONF_BUF); 12192494Ssobomax while (fgetstr(buf, CONF_BUF, conffd) > 0) { 12292494Ssobomax cp = buf; 12392494Ssobomax while ((*cp != '\0') && (isspace(*cp) == 0)) 12492494Ssobomax cp++; 12592494Ssobomax if (*cp != '\0') { 12692494Ssobomax *cp = '\0'; 12792494Ssobomax cp++; 12892494Ssobomax } 12992494Ssobomax while ((*cp != '\0') && (isspace(*cp) != 0)) 13092494Ssobomax cp++; 13192494Ssobomax if (*cp == '\0') 13292494Ssobomax cp = buf; 13392494Ssobomax sf->filesc++; 13492494Ssobomax sf->filesv = realloc(sf->filesv, sizeof(*(sf->filesv)) * sf->filesc); 13592494Ssobomax sf->descsv = realloc(sf->descsv, sizeof(*(sf->descsv)) * sf->filesc); 13692494Ssobomax sf->filesv[sf->filesc - 1] = strdup(buf); 13792494Ssobomax sf->descsv[sf->filesc - 1] = strdup(cp); 13892494Ssobomax } 13992494Ssobomax free(buf); 14092494Ssobomax close(conffd); 14192494Ssobomax 14292494Ssobomax if ((sf->filesc == 0) || ((sf->curfd = open(sf->filesv[0], O_RDONLY)) == -1)) { 14392494Ssobomax split_file_destroy(sf); 14492494Ssobomax return(ENOENT); 14592494Ssobomax } 14692494Ssobomax 14792494Ssobomax /* Looks OK, we'll take it */ 14892494Ssobomax f->f_fsdata = sf; 14992494Ssobomax return (0); 15092494Ssobomax} 15192494Ssobomax 15292494Ssobomaxstatic int 15392494Ssobomaxsplitfs_close(struct open_file *f) 15492494Ssobomax{ 15592494Ssobomax int fd; 15692494Ssobomax struct split_file *sf; 15792494Ssobomax 15892494Ssobomax sf = (struct split_file *)f->f_fsdata; 15992494Ssobomax fd = sf->curfd; 16092494Ssobomax split_file_destroy(sf); 16192494Ssobomax return(close(fd)); 16292494Ssobomax} 16392494Ssobomax 16492494Ssobomaxstatic int 16592494Ssobomaxsplitfs_read(struct open_file *f, void *buf, size_t size, size_t *resid) 16692494Ssobomax{ 16792494Ssobomax int i, nread, totread; 16892494Ssobomax struct split_file *sf; 16992494Ssobomax 17092494Ssobomax sf = (struct split_file *)f->f_fsdata; 17192494Ssobomax totread = 0; 17292494Ssobomax do { 17392494Ssobomax nread = read(sf->curfd, buf, size - totread); 17492494Ssobomax 17592494Ssobomax /* Error? */ 17692494Ssobomax if (nread == -1) 17792494Ssobomax return (errno); 17892494Ssobomax 17992494Ssobomax sf->tot_pos += nread; 18092494Ssobomax sf->file_pos += nread; 18192494Ssobomax totread += nread; 18292494Ssobomax buf += nread; 18392494Ssobomax 18492494Ssobomax if (totread < size) { /* EOF */ 18592494Ssobomax if (sf->curfile == (sf->filesc - 1)) /* Last slice */ 18692494Ssobomax break; 18792494Ssobomax 18892494Ssobomax /* Close previous slice */ 18992494Ssobomax if (close(sf->curfd) != 0) 19092494Ssobomax return (errno); 19192494Ssobomax 19292494Ssobomax sf->curfile++; 19392494Ssobomax for (i = 0;; i++) { 19492494Ssobomax sf->curfd = open(sf->filesv[sf->curfile], O_RDONLY); 19592494Ssobomax if (sf->curfd >= 0) 19692494Ssobomax break; 19792494Ssobomax if ((sf->curfd == -1) && (errno != ENOENT)) 19892494Ssobomax return (errno); 19992494Ssobomax if (i == NTRIES) 20092494Ssobomax return (EIO); 20192494Ssobomax printf("\nInsert disk labelled %s and press any key...", sf->descsv[sf->curfile]); 20292494Ssobomax getchar();putchar('\n'); 20392494Ssobomax } 20492494Ssobomax sf->file_pos = 0; 20592494Ssobomax } 20692494Ssobomax } while (totread < size); 20792494Ssobomax 20892494Ssobomax if (resid != NULL) 20992494Ssobomax *resid = size - totread; 21092494Ssobomax 21192494Ssobomax return (0); 21292494Ssobomax} 21392494Ssobomax 21492494Ssobomaxstatic off_t 21592494Ssobomaxsplitfs_seek(struct open_file *f, off_t offset, int where) 21692494Ssobomax{ 21792494Ssobomax int nread; 21892494Ssobomax size_t resid; 21992494Ssobomax off_t new_pos, seek_by; 22092494Ssobomax struct split_file *sf; 22192494Ssobomax 22292494Ssobomax sf = (struct split_file *)f->f_fsdata; 22392494Ssobomax 22492494Ssobomax seek_by = offset; 22592494Ssobomax switch (where) { 22692494Ssobomax case SEEK_SET: 22792494Ssobomax seek_by -= sf->tot_pos; 22892494Ssobomax break; 22992494Ssobomax case SEEK_CUR: 23092494Ssobomax break; 23192494Ssobomax case SEEK_END: 23292494Ssobomax panic("splitfs_seek: SEEK_END not supported"); 23392494Ssobomax break; 23492494Ssobomax } 23592494Ssobomax 23692494Ssobomax if (seek_by > 0) { 23792494Ssobomax /* 23892494Ssobomax * Seek forward - implemented using splitfs_read(), because otherwise we'll be 23992494Ssobomax * unable to detect that we have crossed slice boundary and hence 24092494Ssobomax * unable to do a long seek crossing that boundary. 24192494Ssobomax */ 24292494Ssobomax void *tmp; 24392494Ssobomax 24492494Ssobomax tmp = malloc(SEEK_BUF); 24592494Ssobomax if (tmp == NULL) 24692494Ssobomax return (-1); 24792494Ssobomax 24892494Ssobomax nread = 0; 24992494Ssobomax for (; seek_by > 0; seek_by -= nread) { 25092494Ssobomax resid = 0; 25192494Ssobomax errno = splitfs_read(f, tmp, min(seek_by, SEEK_BUF), &resid); 25292494Ssobomax nread = min(seek_by, SEEK_BUF) - resid; 25392494Ssobomax if ((errno != 0) || (nread == 0)) 25492494Ssobomax /* Error or EOF */ 25592494Ssobomax break; 25692494Ssobomax } 25792494Ssobomax free(tmp); 25892494Ssobomax if (errno != 0) 25992494Ssobomax return (-1); 26092494Ssobomax } 26192494Ssobomax 26292494Ssobomax if (seek_by != 0) { 26392494Ssobomax /* Seek backward or seek past the boundary of the last slice */ 26492494Ssobomax if (sf->file_pos + seek_by < 0) 26592494Ssobomax panic("splitfs_seek: can't seek past the beginning of the slice"); 26692494Ssobomax new_pos = lseek(sf->curfd, seek_by, SEEK_CUR); 26792494Ssobomax if (new_pos < 0) 26892494Ssobomax return (-1); 26992494Ssobomax sf->tot_pos += new_pos - sf->file_pos; 27092494Ssobomax sf->file_pos = new_pos; 27192494Ssobomax } 27292494Ssobomax 27392494Ssobomax return (sf->tot_pos); 27492494Ssobomax} 27592494Ssobomax 27692494Ssobomaxstatic int 27792494Ssobomaxsplitfs_stat(struct open_file *f, struct stat *sb) 27892494Ssobomax{ 27992494Ssobomax int result; 28092494Ssobomax struct split_file *sf = (struct split_file *)f->f_fsdata; 28192494Ssobomax 28292494Ssobomax /* stat as normal, but indicate that size is unknown */ 28392494Ssobomax if ((result = fstat(sf->curfd, sb)) == 0) 28492494Ssobomax sb->st_size = -1; 28592494Ssobomax return (result); 28692494Ssobomax} 287