splitfs.c revision 92494
1337414Skevans/*
2337414Skevans * Copyright (c) 2002 Maxim Sobolev
3336668Skevans * All rights reserved.
4336668Skevans *
5336668Skevans * Redistribution and use in source and binary forms, with or without
6336668Skevans * modification, are permitted provided that the following conditions
7336668Skevans * are met:
8336668Skevans * 1. Redistributions of source code must retain the above copyright
9336668Skevans *    notice, this list of conditions and the following disclaimer.
10336668Skevans * 2. Redistributions in binary form must reproduce the above copyright
11336668Skevans *    notice, this list of conditions and the following disclaimer in the
12336668Skevans *    documentation and/or other materials provided with the distribution.
13336668Skevans *
14336668Skevans * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15336668Skevans * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16336668Skevans * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17336668Skevans * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18336668Skevans * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19336668Skevans * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20336668Skevans * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21336668Skevans * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22336668Skevans * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23336668Skevans * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24336668Skevans * SUCH DAMAGE.
25336668Skevans */
26336668Skevans
27336668Skevans#include <sys/cdefs.h>
28337416Skevans__FBSDID("$FreeBSD: head/lib/libstand/splitfs.c 92494 2002-03-17 12:18:05Z sobomax $");
29337416Skevans
30337416Skevans#include "stand.h"
31346429Skevans
32346429Skevans#define NTRIES		(3)
33336668Skevans#define CONF_BUF	(512)
34346429Skevans#define SEEK_BUF	(512)
35356593Skevans
36355662Skevansstruct split_file
37355662Skevans{
38355662Skevans    char  **filesv;	/* Filenames */
39336668Skevans    char  **descsv;	/* Descriptions */
40336668Skevans    int	  filesc;	/* Number of parts */
41336668Skevans    int	  curfile;	/* Current file number */
42336668Skevans    int	  curfd;	/* Current file descriptor */
43336668Skevans    off_t tot_pos;	/* Offset from the beginning of the sequence */
44336668Skevans    off_t file_pos;	/* Offset from the beginning of the slice */
45336668Skevans};
46336668Skevans
47336668Skevansstatic int	splitfs_open(const char *path, struct open_file *f);
48336668Skevansstatic int	splitfs_close(struct open_file *f);
49336668Skevansstatic int	splitfs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
50356593Skevansstatic off_t	splitfs_seek(struct open_file *f, off_t offset, int where);
51356593Skevansstatic int	splitfs_stat(struct open_file *f, struct stat *sb);
52356593Skevans
53356593Skevansstruct fs_ops splitfs_fsops = {
54356593Skevans    "split",
55346429Skevans    splitfs_open,
56356593Skevans    splitfs_close,
57356593Skevans    splitfs_read,
58356593Skevans    null_write,
59356593Skevans    splitfs_seek,
60346429Skevans    splitfs_stat,
61346429Skevans    null_readdir
62337596Skevans};
63337592Skevans
64337592Skevansstatic void
65337592Skevanssplit_file_destroy(struct split_file *sf)
66337596Skevans{
67337592Skevans     int i;
68346429Skevans
69346429Skevans     if (sf->filesc > 0) {
70346429Skevans	for (i = 0; i < sf->filesc; i++) {
71336668Skevans	    free(sf->filesv[i]);
72336699Skevans	    free(sf->descsv[i]);
73336699Skevans	}
74336699Skevans	free(sf->filesv);
75336699Skevans	free(sf->descsv);
76346429Skevans     }
77336699Skevans     free(sf);
78346429Skevans}
79355662Skevans
80346429Skevansstatic int
81336699Skevanssplitfs_open(const char *fname, struct open_file *f)
82346429Skevans{
83346429Skevans    char *buf, *confname, *cp;
84346429Skevans    int	conffd;
85346429Skevans    struct split_file *sf;
86346429Skevans    struct stat sb;
87346429Skevans
88346429Skevans    printf("%s\n", fname);
89346429Skevans    /* Have to be in "just read it" mode */
90346429Skevans    if (f->f_flags != F_READ)
91346429Skevans	return(EPERM);
92346429Skevans
93336699Skevans    /* If the name already ends in `.split', ignore it */
94346429Skevans    if ((cp = strrchr(fname, '.')) && (!strcmp(cp, ".split")))
95346429Skevans	return(ENOENT);
96336699Skevans
97336699Skevans    /* Construct new name */
98346429Skevans    confname = malloc(strlen(fname) + 7);
99346429Skevans    sprintf(confname, "%s.split", fname);
100336699Skevans
101336699Skevans    /* Try to open the configuration file */
102336699Skevans    conffd = open(confname, O_RDONLY);
103336699Skevans    free(confname);
104336668Skevans    if (conffd == -1)
105336668Skevans	return(ENOENT);
106336668Skevans
107336668Skevans    if (fstat(conffd, &sb) < 0) {
108346429Skevans	printf("splitfs_open: stat failed\n");
109336668Skevans	close(conffd);
110346429Skevans	return(ENOENT);
111336668Skevans    }
112336696Skevans    if (!S_ISREG(sb.st_mode)) {
113336696Skevans	printf("splitfs_open: not a file\n");
114336668Skevans	close(conffd);
115336696Skevans	return(EISDIR);			/* best guess */
116336696Skevans    }
117336699Skevans
118336696Skevans    /* Allocate a split_file structure, populate it from the config file */
119336696Skevans    sf = malloc(sizeof(struct split_file));
120336668Skevans    bzero(sf, sizeof(struct split_file));
121336696Skevans    buf = malloc(CONF_BUF);
122336696Skevans    while (fgetstr(buf, CONF_BUF, conffd) > 0) {
123336668Skevans	cp = buf;
124346429Skevans	while ((*cp != '\0') && (isspace(*cp) == 0))
125346429Skevans	    cp++;
126346429Skevans	if (*cp != '\0') {
127346429Skevans	    *cp = '\0';
128346429Skevans	    cp++;
129346429Skevans	}
130346429Skevans	while ((*cp != '\0') && (isspace(*cp) != 0))
131346429Skevans	    cp++;
132346429Skevans	if (*cp == '\0')
133346429Skevans	    cp = buf;
134346429Skevans	sf->filesc++;
135346429Skevans	sf->filesv = realloc(sf->filesv, sizeof(*(sf->filesv)) * sf->filesc);
136346429Skevans	sf->descsv = realloc(sf->descsv, sizeof(*(sf->descsv)) * sf->filesc);
137346429Skevans	sf->filesv[sf->filesc - 1] = strdup(buf);
138346429Skevans	sf->descsv[sf->filesc - 1] = strdup(cp);
139346429Skevans    }
140346429Skevans    free(buf);
141346429Skevans    close(conffd);
142336668Skevans
143336696Skevans    if ((sf->filesc == 0) || ((sf->curfd = open(sf->filesv[0], O_RDONLY)) == -1)) {
144336696Skevans	split_file_destroy(sf);
145336668Skevans	return(ENOENT);
146336696Skevans    }
147336696Skevans
148336696Skevans    /* Looks OK, we'll take it */
149336696Skevans    f->f_fsdata = sf;
150336696Skevans    return (0);
151346429Skevans}
152336696Skevans
153336696Skevansstatic int
154346429Skevanssplitfs_close(struct open_file *f)
155346429Skevans{
156336696Skevans    int fd;
157336696Skevans    struct split_file *sf;
158346429Skevans
159336696Skevans    sf = (struct split_file *)f->f_fsdata;
160336696Skevans    fd = sf->curfd;
161346429Skevans    split_file_destroy(sf);
162346429Skevans    return(close(fd));
163346429Skevans}
164346429Skevans
165336696Skevansstatic int
166336668Skevanssplitfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
167336696Skevans{
168336696Skevans    int i, nread, totread;
169336696Skevans    struct split_file *sf;
170336696Skevans
171336696Skevans    sf = (struct split_file *)f->f_fsdata;
172336696Skevans    totread = 0;
173336696Skevans    do {
174336696Skevans	nread = read(sf->curfd, buf, size - totread);
175336696Skevans
176336696Skevans	/* Error? */
177336668Skevans	if (nread == -1)
178336668Skevans	    return (errno);
179336668Skevans
180336668Skevans	sf->tot_pos += nread;
181336668Skevans	sf->file_pos += nread;
182336668Skevans	totread += nread;
183336668Skevans	buf += nread;
184336668Skevans
185336668Skevans	if (totread < size) {				/* EOF */
186336701Skevans	    if (sf->curfile == (sf->filesc - 1))	/* Last slice */
187336696Skevans		break;
188336696Skevans
189336668Skevans	    /* Close previous slice */
190336668Skevans	    if (close(sf->curfd) != 0)
191336668Skevans		return (errno);
192336668Skevans
193337343Skevans	    sf->curfile++;
194337343Skevans	    for (i = 0;; i++) {
195337343Skevans		sf->curfd = open(sf->filesv[sf->curfile], O_RDONLY);
196337343Skevans		if (sf->curfd >= 0)
197337343Skevans		    break;
198337343Skevans		if ((sf->curfd == -1) && (errno != ENOENT))
199336668Skevans		    return (errno);
200337343Skevans		if (i == NTRIES)
201337343Skevans		    return (EIO);
202337343Skevans		printf("\nInsert disk labelled %s and press any key...", sf->descsv[sf->curfile]);
203356593Skevans		getchar();putchar('\n');
204356593Skevans	    }
205356593Skevans	    sf->file_pos = 0;
206356593Skevans	}
207356593Skevans    } while (totread < size);
208356593Skevans
209356593Skevans    if (resid != NULL)
210356593Skevans	*resid = size - totread;
211356593Skevans
212356593Skevans    return (0);
213356593Skevans}
214356593Skevans
215356593Skevansstatic off_t
216356593Skevanssplitfs_seek(struct open_file *f, off_t offset, int where)
217356593Skevans{
218356593Skevans    int nread;
219356593Skevans    size_t resid;
220356593Skevans    off_t new_pos, seek_by;
221356593Skevans    struct split_file *sf;
222356593Skevans
223356593Skevans    sf = (struct split_file *)f->f_fsdata;
224356593Skevans
225356593Skevans    seek_by = offset;
226356593Skevans    switch (where) {
227356593Skevans    case SEEK_SET:
228356593Skevans	seek_by -= sf->tot_pos;
229356593Skevans	break;
230356593Skevans    case SEEK_CUR:
231337409Skevans	break;
232356593Skevans    case SEEK_END:
233356593Skevans	panic("splitfs_seek: SEEK_END not supported");
234356593Skevans	break;
235356593Skevans    }
236356593Skevans
237356593Skevans    if (seek_by > 0) {
238356593Skevans	/*
239356593Skevans	 * Seek forward - implemented using splitfs_read(), because otherwise we'll be
240356593Skevans	 * unable to detect that we have crossed slice boundary and hence
241356593Skevans	 * unable to do a long seek crossing that boundary.
242356593Skevans	 */
243356593Skevans	void *tmp;
244356593Skevans
245356593Skevans	tmp = malloc(SEEK_BUF);
246356593Skevans	if (tmp == NULL)
247356593Skevans	    return (-1);
248356593Skevans
249356593Skevans	nread = 0;
250356593Skevans	for (; seek_by > 0; seek_by -= nread) {
251356593Skevans	    resid = 0;
252356593Skevans	    errno = splitfs_read(f, tmp, min(seek_by, SEEK_BUF), &resid);
253356593Skevans	    nread = min(seek_by, SEEK_BUF) - resid;
254356593Skevans	    if ((errno != 0) || (nread == 0))
255356593Skevans		/* Error or EOF */
256356593Skevans		break;
257356593Skevans	}
258356593Skevans	free(tmp);
259356593Skevans	if (errno != 0)
260356593Skevans	    return (-1);
261356593Skevans    }
262356593Skevans
263356593Skevans    if (seek_by != 0) {
264356593Skevans	/* Seek backward or seek past the boundary of the last slice */
265356593Skevans	if (sf->file_pos + seek_by < 0)
266356593Skevans	    panic("splitfs_seek: can't seek past the beginning of the slice");
267356593Skevans	new_pos = lseek(sf->curfd, seek_by, SEEK_CUR);
268356593Skevans	if (new_pos < 0)
269356593Skevans	    return (-1);
270356593Skevans	sf->tot_pos += new_pos - sf->file_pos;
271356593Skevans	sf->file_pos = new_pos;
272356593Skevans    }
273356593Skevans
274356593Skevans    return (sf->tot_pos);
275356593Skevans}
276356593Skevans
277356593Skevansstatic int
278356593Skevanssplitfs_stat(struct open_file *f, struct stat *sb)
279356593Skevans{
280356593Skevans    int	result;
281356593Skevans    struct split_file *sf = (struct split_file *)f->f_fsdata;
282356593Skevans
283356593Skevans    /* stat as normal, but indicate that size is unknown */
284356593Skevans    if ((result = fstat(sf->curfd, sb)) == 0)
285356593Skevans	sb->st_size = -1;
286356593Skevans    return (result);
287356593Skevans}
288356593Skevans