splitfs.c revision 124811
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 124811 2004-01-21 20:12:23Z 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 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{ 19592494Ssobomax int i, nread, totread; 19692494Ssobomax struct split_file *sf; 19792494Ssobomax 19892494Ssobomax sf = (struct split_file *)f->f_fsdata; 19992494Ssobomax totread = 0; 20092494Ssobomax do { 20192494Ssobomax nread = read(sf->curfd, buf, size - totread); 20292494Ssobomax 20392494Ssobomax /* Error? */ 20492494Ssobomax if (nread == -1) 20592494Ssobomax return (errno); 20692494Ssobomax 20792494Ssobomax sf->tot_pos += nread; 20892494Ssobomax sf->file_pos += nread; 20992494Ssobomax totread += nread; 21092494Ssobomax buf += nread; 21192494Ssobomax 21292494Ssobomax if (totread < size) { /* EOF */ 21392494Ssobomax if (sf->curfile == (sf->filesc - 1)) /* Last slice */ 21492494Ssobomax break; 21592494Ssobomax 21692494Ssobomax /* Close previous slice */ 21792494Ssobomax if (close(sf->curfd) != 0) 21892494Ssobomax return (errno); 21992494Ssobomax 22092494Ssobomax sf->curfile++; 221124572Sjhb errno = split_openfile(sf); 222124572Sjhb if (errno) 22392494Ssobomax return (errno); 22492494Ssobomax } 22592494Ssobomax } while (totread < size); 22692494Ssobomax 22792494Ssobomax if (resid != NULL) 22892494Ssobomax *resid = size - totread; 22992494Ssobomax 23092494Ssobomax return (0); 23192494Ssobomax} 23292494Ssobomax 23392494Ssobomaxstatic off_t 23492494Ssobomaxsplitfs_seek(struct open_file *f, off_t offset, int where) 23592494Ssobomax{ 23692494Ssobomax int nread; 23792494Ssobomax size_t resid; 23892494Ssobomax off_t new_pos, seek_by; 23992494Ssobomax struct split_file *sf; 24092494Ssobomax 24192494Ssobomax sf = (struct split_file *)f->f_fsdata; 24292494Ssobomax 24392494Ssobomax seek_by = offset; 24492494Ssobomax switch (where) { 24592494Ssobomax case SEEK_SET: 24692494Ssobomax seek_by -= sf->tot_pos; 24792494Ssobomax break; 24892494Ssobomax case SEEK_CUR: 24992494Ssobomax break; 25092494Ssobomax case SEEK_END: 25192494Ssobomax panic("splitfs_seek: SEEK_END not supported"); 25292494Ssobomax break; 253124811Sjhb default: 254124811Sjhb errno = EINVAL; 255124811Sjhb return (-1); 25692494Ssobomax } 25792494Ssobomax 25892494Ssobomax if (seek_by > 0) { 25992494Ssobomax /* 26092494Ssobomax * Seek forward - implemented using splitfs_read(), because otherwise we'll be 26192494Ssobomax * unable to detect that we have crossed slice boundary and hence 26292494Ssobomax * unable to do a long seek crossing that boundary. 26392494Ssobomax */ 26492494Ssobomax void *tmp; 26592494Ssobomax 26692494Ssobomax tmp = malloc(SEEK_BUF); 267124811Sjhb if (tmp == NULL) { 268124811Sjhb errno = ENOMEM; 26992494Ssobomax return (-1); 270124811Sjhb } 27192494Ssobomax 27292494Ssobomax nread = 0; 27392494Ssobomax for (; seek_by > 0; seek_by -= nread) { 27492494Ssobomax resid = 0; 27592494Ssobomax errno = splitfs_read(f, tmp, min(seek_by, SEEK_BUF), &resid); 27692494Ssobomax nread = min(seek_by, SEEK_BUF) - resid; 27792494Ssobomax if ((errno != 0) || (nread == 0)) 27892494Ssobomax /* Error or EOF */ 27992494Ssobomax break; 28092494Ssobomax } 28192494Ssobomax free(tmp); 28292494Ssobomax if (errno != 0) 28392494Ssobomax return (-1); 28492494Ssobomax } 28592494Ssobomax 28692494Ssobomax if (seek_by != 0) { 28792494Ssobomax /* Seek backward or seek past the boundary of the last slice */ 28892494Ssobomax if (sf->file_pos + seek_by < 0) 28992494Ssobomax panic("splitfs_seek: can't seek past the beginning of the slice"); 29092494Ssobomax new_pos = lseek(sf->curfd, seek_by, SEEK_CUR); 291124811Sjhb if (new_pos < 0) { 292124811Sjhb errno = EINVAL; 29392494Ssobomax return (-1); 294124811Sjhb } 29592494Ssobomax sf->tot_pos += new_pos - sf->file_pos; 29692494Ssobomax sf->file_pos = new_pos; 29792494Ssobomax } 29892494Ssobomax 29992494Ssobomax return (sf->tot_pos); 30092494Ssobomax} 30192494Ssobomax 30292494Ssobomaxstatic int 30392494Ssobomaxsplitfs_stat(struct open_file *f, struct stat *sb) 30492494Ssobomax{ 30592494Ssobomax int result; 30692494Ssobomax struct split_file *sf = (struct split_file *)f->f_fsdata; 30792494Ssobomax 30892494Ssobomax /* stat as normal, but indicate that size is unknown */ 30992494Ssobomax if ((result = fstat(sf->curfd, sb)) == 0) 31092494Ssobomax sb->st_size = -1; 31192494Ssobomax return (result); 31292494Ssobomax} 313