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$"); 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 47124572Sjhbstatic int split_openfile(struct split_file *sf); 4892494Ssobomaxstatic int splitfs_open(const char *path, struct open_file *f); 4992494Ssobomaxstatic int splitfs_close(struct open_file *f); 5092494Ssobomaxstatic int splitfs_read(struct open_file *f, void *buf, size_t size, size_t *resid); 5192494Ssobomaxstatic off_t splitfs_seek(struct open_file *f, off_t offset, int where); 5292494Ssobomaxstatic int splitfs_stat(struct open_file *f, struct stat *sb); 5392494Ssobomax 5492494Ssobomaxstruct fs_ops splitfs_fsops = { 5592494Ssobomax "split", 5692494Ssobomax splitfs_open, 5792494Ssobomax splitfs_close, 5892494Ssobomax splitfs_read, 5992494Ssobomax null_write, 6092494Ssobomax splitfs_seek, 6192494Ssobomax splitfs_stat, 6292494Ssobomax null_readdir 6392494Ssobomax}; 6492494Ssobomax 6592494Ssobomaxstatic void 6692494Ssobomaxsplit_file_destroy(struct split_file *sf) 6792494Ssobomax{ 68124571Sjhb int i; 6992494Ssobomax 70124571Sjhb if (sf->filesc > 0) { 7192494Ssobomax for (i = 0; i < sf->filesc; i++) { 7292494Ssobomax free(sf->filesv[i]); 7392494Ssobomax free(sf->descsv[i]); 7492494Ssobomax } 7592494Ssobomax free(sf->filesv); 7692494Ssobomax free(sf->descsv); 77124571Sjhb } 78124571Sjhb free(sf); 7992494Ssobomax} 8092494Ssobomax 8192494Ssobomaxstatic int 82124572Sjhbsplit_openfile(struct split_file *sf) 83124572Sjhb{ 84124572Sjhb int i; 85124572Sjhb 86124572Sjhb for (i = 0;; i++) { 87124572Sjhb sf->curfd = open(sf->filesv[sf->curfile], O_RDONLY); 88124572Sjhb if (sf->curfd >= 0) 89124572Sjhb break; 90124572Sjhb if ((sf->curfd == -1) && (errno != ENOENT)) 91124572Sjhb return (errno); 92124572Sjhb if (i == NTRIES) 93124572Sjhb return (EIO); 94124572Sjhb printf("\nInsert disk labelled %s and press any key...", 95124572Sjhb sf->descsv[sf->curfile]); 96124572Sjhb getchar(); 97124572Sjhb putchar('\n'); 98124572Sjhb } 99124572Sjhb sf->file_pos = 0; 100124572Sjhb return (0); 101124572Sjhb} 102124572Sjhb 103124572Sjhbstatic int 10492494Ssobomaxsplitfs_open(const char *fname, struct open_file *f) 10592494Ssobomax{ 10692494Ssobomax char *buf, *confname, *cp; 10792494Ssobomax int conffd; 10892494Ssobomax struct split_file *sf; 10992494Ssobomax struct stat sb; 11092494Ssobomax 11192494Ssobomax /* Have to be in "just read it" mode */ 11292494Ssobomax if (f->f_flags != F_READ) 11392494Ssobomax return(EPERM); 11492494Ssobomax 11592494Ssobomax /* If the name already ends in `.split', ignore it */ 11692494Ssobomax if ((cp = strrchr(fname, '.')) && (!strcmp(cp, ".split"))) 11792494Ssobomax return(ENOENT); 11892494Ssobomax 11992494Ssobomax /* Construct new name */ 12092494Ssobomax confname = malloc(strlen(fname) + 7); 12192494Ssobomax sprintf(confname, "%s.split", fname); 12292494Ssobomax 12392494Ssobomax /* Try to open the configuration file */ 12492494Ssobomax conffd = open(confname, O_RDONLY); 12592494Ssobomax free(confname); 12692494Ssobomax if (conffd == -1) 12792494Ssobomax return(ENOENT); 12892494Ssobomax 12992494Ssobomax if (fstat(conffd, &sb) < 0) { 13092494Ssobomax printf("splitfs_open: stat failed\n"); 13192494Ssobomax close(conffd); 13292494Ssobomax return(ENOENT); 13392494Ssobomax } 13492494Ssobomax if (!S_ISREG(sb.st_mode)) { 13592494Ssobomax printf("splitfs_open: not a file\n"); 13692494Ssobomax close(conffd); 13792494Ssobomax return(EISDIR); /* best guess */ 13892494Ssobomax } 13992494Ssobomax 14092494Ssobomax /* Allocate a split_file structure, populate it from the config file */ 14192494Ssobomax sf = malloc(sizeof(struct split_file)); 14292494Ssobomax bzero(sf, sizeof(struct split_file)); 14392494Ssobomax buf = malloc(CONF_BUF); 14492494Ssobomax while (fgetstr(buf, CONF_BUF, conffd) > 0) { 14592494Ssobomax cp = buf; 14692494Ssobomax while ((*cp != '\0') && (isspace(*cp) == 0)) 14792494Ssobomax cp++; 14892494Ssobomax if (*cp != '\0') { 14992494Ssobomax *cp = '\0'; 15092494Ssobomax cp++; 15192494Ssobomax } 15292494Ssobomax while ((*cp != '\0') && (isspace(*cp) != 0)) 15392494Ssobomax cp++; 15492494Ssobomax if (*cp == '\0') 15592494Ssobomax cp = buf; 15692494Ssobomax sf->filesc++; 15792494Ssobomax sf->filesv = realloc(sf->filesv, sizeof(*(sf->filesv)) * sf->filesc); 15892494Ssobomax sf->descsv = realloc(sf->descsv, sizeof(*(sf->descsv)) * sf->filesc); 15992494Ssobomax sf->filesv[sf->filesc - 1] = strdup(buf); 16092494Ssobomax sf->descsv[sf->filesc - 1] = strdup(cp); 16192494Ssobomax } 16292494Ssobomax free(buf); 16392494Ssobomax close(conffd); 16492494Ssobomax 165124572Sjhb if (sf->filesc == 0) { 16692494Ssobomax split_file_destroy(sf); 16792494Ssobomax return(ENOENT); 16892494Ssobomax } 169124572Sjhb errno = split_openfile(sf); 170124572Sjhb if (errno != 0) { 171124572Sjhb split_file_destroy(sf); 172124572Sjhb return(ENOENT); 173124572Sjhb } 17492494Ssobomax 17592494Ssobomax /* Looks OK, we'll take it */ 17692494Ssobomax f->f_fsdata = sf; 17792494Ssobomax return (0); 17892494Ssobomax} 17992494Ssobomax 18092494Ssobomaxstatic int 18192494Ssobomaxsplitfs_close(struct open_file *f) 18292494Ssobomax{ 18392494Ssobomax int fd; 18492494Ssobomax struct split_file *sf; 18592494Ssobomax 18692494Ssobomax sf = (struct split_file *)f->f_fsdata; 18792494Ssobomax fd = sf->curfd; 18892494Ssobomax split_file_destroy(sf); 18992494Ssobomax return(close(fd)); 19092494Ssobomax} 19192494Ssobomax 19292494Ssobomaxstatic int 19392494Ssobomaxsplitfs_read(struct open_file *f, void *buf, size_t size, size_t *resid) 19492494Ssobomax{ 195146443Scharnier ssize_t nread; 196146443Scharnier size_t totread; 19792494Ssobomax struct split_file *sf; 19892494Ssobomax 19992494Ssobomax sf = (struct split_file *)f->f_fsdata; 20092494Ssobomax totread = 0; 20192494Ssobomax do { 20292494Ssobomax nread = read(sf->curfd, buf, size - totread); 20392494Ssobomax 20492494Ssobomax /* Error? */ 20592494Ssobomax if (nread == -1) 20692494Ssobomax return (errno); 20792494Ssobomax 20892494Ssobomax sf->tot_pos += nread; 20992494Ssobomax sf->file_pos += nread; 21092494Ssobomax totread += nread; 211136093Sstefanf buf = (char *)buf + nread; 21292494Ssobomax 21392494Ssobomax if (totread < size) { /* EOF */ 21492494Ssobomax if (sf->curfile == (sf->filesc - 1)) /* Last slice */ 21592494Ssobomax break; 21692494Ssobomax 21792494Ssobomax /* Close previous slice */ 21892494Ssobomax if (close(sf->curfd) != 0) 21992494Ssobomax return (errno); 22092494Ssobomax 22192494Ssobomax sf->curfile++; 222124572Sjhb errno = split_openfile(sf); 223124572Sjhb if (errno) 22492494Ssobomax return (errno); 22592494Ssobomax } 22692494Ssobomax } while (totread < size); 22792494Ssobomax 22892494Ssobomax if (resid != NULL) 22992494Ssobomax *resid = size - totread; 23092494Ssobomax 23192494Ssobomax return (0); 23292494Ssobomax} 23392494Ssobomax 23492494Ssobomaxstatic off_t 23592494Ssobomaxsplitfs_seek(struct open_file *f, off_t offset, int where) 23692494Ssobomax{ 23792494Ssobomax int nread; 23892494Ssobomax size_t resid; 23992494Ssobomax off_t new_pos, seek_by; 24092494Ssobomax struct split_file *sf; 24192494Ssobomax 24292494Ssobomax sf = (struct split_file *)f->f_fsdata; 24392494Ssobomax 24492494Ssobomax seek_by = offset; 24592494Ssobomax switch (where) { 24692494Ssobomax case SEEK_SET: 24792494Ssobomax seek_by -= sf->tot_pos; 24892494Ssobomax break; 24992494Ssobomax case SEEK_CUR: 25092494Ssobomax break; 25192494Ssobomax case SEEK_END: 25292494Ssobomax panic("splitfs_seek: SEEK_END not supported"); 25392494Ssobomax break; 254124811Sjhb default: 255124811Sjhb errno = EINVAL; 256124811Sjhb return (-1); 25792494Ssobomax } 25892494Ssobomax 25992494Ssobomax if (seek_by > 0) { 26092494Ssobomax /* 26192494Ssobomax * Seek forward - implemented using splitfs_read(), because otherwise we'll be 26292494Ssobomax * unable to detect that we have crossed slice boundary and hence 26392494Ssobomax * unable to do a long seek crossing that boundary. 26492494Ssobomax */ 26592494Ssobomax void *tmp; 26692494Ssobomax 26792494Ssobomax tmp = malloc(SEEK_BUF); 268124811Sjhb if (tmp == NULL) { 269124811Sjhb errno = ENOMEM; 27092494Ssobomax return (-1); 271124811Sjhb } 27292494Ssobomax 27392494Ssobomax nread = 0; 27492494Ssobomax for (; seek_by > 0; seek_by -= nread) { 27592494Ssobomax resid = 0; 27692494Ssobomax errno = splitfs_read(f, tmp, min(seek_by, SEEK_BUF), &resid); 27792494Ssobomax nread = min(seek_by, SEEK_BUF) - resid; 27892494Ssobomax if ((errno != 0) || (nread == 0)) 27992494Ssobomax /* Error or EOF */ 28092494Ssobomax break; 28192494Ssobomax } 28292494Ssobomax free(tmp); 28392494Ssobomax if (errno != 0) 28492494Ssobomax return (-1); 28592494Ssobomax } 28692494Ssobomax 28792494Ssobomax if (seek_by != 0) { 28892494Ssobomax /* Seek backward or seek past the boundary of the last slice */ 28992494Ssobomax if (sf->file_pos + seek_by < 0) 29092494Ssobomax panic("splitfs_seek: can't seek past the beginning of the slice"); 29192494Ssobomax new_pos = lseek(sf->curfd, seek_by, SEEK_CUR); 292124811Sjhb if (new_pos < 0) { 293124811Sjhb errno = EINVAL; 29492494Ssobomax return (-1); 295124811Sjhb } 29692494Ssobomax sf->tot_pos += new_pos - sf->file_pos; 29792494Ssobomax sf->file_pos = new_pos; 29892494Ssobomax } 29992494Ssobomax 30092494Ssobomax return (sf->tot_pos); 30192494Ssobomax} 30292494Ssobomax 30392494Ssobomaxstatic int 30492494Ssobomaxsplitfs_stat(struct open_file *f, struct stat *sb) 30592494Ssobomax{ 30692494Ssobomax int result; 30792494Ssobomax struct split_file *sf = (struct split_file *)f->f_fsdata; 30892494Ssobomax 30992494Ssobomax /* stat as normal, but indicate that size is unknown */ 31092494Ssobomax if ((result = fstat(sf->curfd, sb)) == 0) 31192494Ssobomax sb->st_size = -1; 31292494Ssobomax return (result); 31392494Ssobomax} 314