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