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