splitfs.c revision 92495
1/*
2 * Copyright (c) 2002 Maxim Sobolev
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/lib/libstand/splitfs.c 92494 2002-03-17 12:18:05Z sobomax $");
29
30#include "stand.h"
31
32#define NTRIES		(3)
33#define CONF_BUF	(512)
34#define SEEK_BUF	(512)
35
36struct split_file
37{
38    char  **filesv;	/* Filenames */
39    char  **descsv;	/* Descriptions */
40    int	  filesc;	/* Number of parts */
41    int	  curfile;	/* Current file number */
42    int	  curfd;	/* Current file descriptor */
43    off_t tot_pos;	/* Offset from the beginning of the sequence */
44    off_t file_pos;	/* Offset from the beginning of the slice */
45};
46
47static int	splitfs_open(const char *path, struct open_file *f);
48static int	splitfs_close(struct open_file *f);
49static int	splitfs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
50static off_t	splitfs_seek(struct open_file *f, off_t offset, int where);
51static int	splitfs_stat(struct open_file *f, struct stat *sb);
52
53struct fs_ops splitfs_fsops = {
54    "split",
55    splitfs_open,
56    splitfs_close,
57    splitfs_read,
58    null_write,
59    splitfs_seek,
60    splitfs_stat,
61    null_readdir
62};
63
64static void
65split_file_destroy(struct split_file *sf)
66{
67     int i;
68
69     if (sf->filesc > 0) {
70	for (i = 0; i < sf->filesc; i++) {
71	    free(sf->filesv[i]);
72	    free(sf->descsv[i]);
73	}
74	free(sf->filesv);
75	free(sf->descsv);
76     }
77     free(sf);
78}
79
80static int
81splitfs_open(const char *fname, struct open_file *f)
82{
83    char *buf, *confname, *cp;
84    int	conffd;
85    struct split_file *sf;
86    struct stat sb;
87
88    printf("%s\n", fname);
89    /* Have to be in "just read it" mode */
90    if (f->f_flags != F_READ)
91	return(EPERM);
92
93    /* If the name already ends in `.split', ignore it */
94    if ((cp = strrchr(fname, '.')) && (!strcmp(cp, ".split")))
95	return(ENOENT);
96
97    /* Construct new name */
98    confname = malloc(strlen(fname) + 7);
99    sprintf(confname, "%s.split", fname);
100
101    /* Try to open the configuration file */
102    conffd = open(confname, O_RDONLY);
103    free(confname);
104    if (conffd == -1)
105	return(ENOENT);
106
107    if (fstat(conffd, &sb) < 0) {
108	printf("splitfs_open: stat failed\n");
109	close(conffd);
110	return(ENOENT);
111    }
112    if (!S_ISREG(sb.st_mode)) {
113	printf("splitfs_open: not a file\n");
114	close(conffd);
115	return(EISDIR);			/* best guess */
116    }
117
118    /* Allocate a split_file structure, populate it from the config file */
119    sf = malloc(sizeof(struct split_file));
120    bzero(sf, sizeof(struct split_file));
121    buf = malloc(CONF_BUF);
122    while (fgetstr(buf, CONF_BUF, conffd) > 0) {
123	cp = buf;
124	while ((*cp != '\0') && (isspace(*cp) == 0))
125	    cp++;
126	if (*cp != '\0') {
127	    *cp = '\0';
128	    cp++;
129	}
130	while ((*cp != '\0') && (isspace(*cp) != 0))
131	    cp++;
132	if (*cp == '\0')
133	    cp = buf;
134	sf->filesc++;
135	sf->filesv = realloc(sf->filesv, sizeof(*(sf->filesv)) * sf->filesc);
136	sf->descsv = realloc(sf->descsv, sizeof(*(sf->descsv)) * sf->filesc);
137	sf->filesv[sf->filesc - 1] = strdup(buf);
138	sf->descsv[sf->filesc - 1] = strdup(cp);
139    }
140    free(buf);
141    close(conffd);
142
143    if ((sf->filesc == 0) || ((sf->curfd = open(sf->filesv[0], O_RDONLY)) == -1)) {
144	split_file_destroy(sf);
145	return(ENOENT);
146    }
147
148    /* Looks OK, we'll take it */
149    f->f_fsdata = sf;
150    return (0);
151}
152
153static int
154splitfs_close(struct open_file *f)
155{
156    int fd;
157    struct split_file *sf;
158
159    sf = (struct split_file *)f->f_fsdata;
160    fd = sf->curfd;
161    split_file_destroy(sf);
162    return(close(fd));
163}
164
165static int
166splitfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
167{
168    int i, nread, totread;
169    struct split_file *sf;
170
171    sf = (struct split_file *)f->f_fsdata;
172    totread = 0;
173    do {
174	nread = read(sf->curfd, buf, size - totread);
175
176	/* Error? */
177	if (nread == -1)
178	    return (errno);
179
180	sf->tot_pos += nread;
181	sf->file_pos += nread;
182	totread += nread;
183	buf += nread;
184
185	if (totread < size) {				/* EOF */
186	    if (sf->curfile == (sf->filesc - 1))	/* Last slice */
187		break;
188
189	    /* Close previous slice */
190	    if (close(sf->curfd) != 0)
191		return (errno);
192
193	    sf->curfile++;
194	    for (i = 0;; i++) {
195		sf->curfd = open(sf->filesv[sf->curfile], O_RDONLY);
196		if (sf->curfd >= 0)
197		    break;
198		if ((sf->curfd == -1) && (errno != ENOENT))
199		    return (errno);
200		if (i == NTRIES)
201		    return (EIO);
202		printf("\nInsert disk labelled %s and press any key...", sf->descsv[sf->curfile]);
203		getchar();putchar('\n');
204	    }
205	    sf->file_pos = 0;
206	}
207    } while (totread < size);
208
209    if (resid != NULL)
210	*resid = size - totread;
211
212    return (0);
213}
214
215static off_t
216splitfs_seek(struct open_file *f, off_t offset, int where)
217{
218    int nread;
219    size_t resid;
220    off_t new_pos, seek_by;
221    struct split_file *sf;
222
223    sf = (struct split_file *)f->f_fsdata;
224
225    seek_by = offset;
226    switch (where) {
227    case SEEK_SET:
228	seek_by -= sf->tot_pos;
229	break;
230    case SEEK_CUR:
231	break;
232    case SEEK_END:
233	panic("splitfs_seek: SEEK_END not supported");
234	break;
235    }
236
237    if (seek_by > 0) {
238	/*
239	 * Seek forward - implemented using splitfs_read(), because otherwise we'll be
240	 * unable to detect that we have crossed slice boundary and hence
241	 * unable to do a long seek crossing that boundary.
242	 */
243	void *tmp;
244
245	tmp = malloc(SEEK_BUF);
246	if (tmp == NULL)
247	    return (-1);
248
249	nread = 0;
250	for (; seek_by > 0; seek_by -= nread) {
251	    resid = 0;
252	    errno = splitfs_read(f, tmp, min(seek_by, SEEK_BUF), &resid);
253	    nread = min(seek_by, SEEK_BUF) - resid;
254	    if ((errno != 0) || (nread == 0))
255		/* Error or EOF */
256		break;
257	}
258	free(tmp);
259	if (errno != 0)
260	    return (-1);
261    }
262
263    if (seek_by != 0) {
264	/* Seek backward or seek past the boundary of the last slice */
265	if (sf->file_pos + seek_by < 0)
266	    panic("splitfs_seek: can't seek past the beginning of the slice");
267	new_pos = lseek(sf->curfd, seek_by, SEEK_CUR);
268	if (new_pos < 0)
269	    return (-1);
270	sf->tot_pos += new_pos - sf->file_pos;
271	sf->file_pos = new_pos;
272    }
273
274    return (sf->tot_pos);
275}
276
277static int
278splitfs_stat(struct open_file *f, struct stat *sb)
279{
280    int	result;
281    struct split_file *sf = (struct split_file *)f->f_fsdata;
282
283    /* stat as normal, but indicate that size is unknown */
284    if ((result = fstat(sf->curfd, sb)) == 0)
285	sb->st_size = -1;
286    return (result);
287}
288