splitfs.c revision 124571
1209878Snwhitehorn/*
2209878Snwhitehorn * Copyright (c) 2002 Maxim Sobolev
3209878Snwhitehorn * All rights reserved.
4209878Snwhitehorn *
5209878Snwhitehorn * Redistribution and use in source and binary forms, with or without
6209878Snwhitehorn * modification, are permitted provided that the following conditions
7209878Snwhitehorn * are met:
8209878Snwhitehorn * 1. Redistributions of source code must retain the above copyright
9209878Snwhitehorn *    notice, this list of conditions and the following disclaimer.
10209878Snwhitehorn * 2. Redistributions in binary form must reproduce the above copyright
11209878Snwhitehorn *    notice, this list of conditions and the following disclaimer in the
12209878Snwhitehorn *    documentation and/or other materials provided with the distribution.
13209878Snwhitehorn *
14209878Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15209878Snwhitehorn * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16209878Snwhitehorn * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17209878Snwhitehorn * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18209878Snwhitehorn * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19209878Snwhitehorn * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20209878Snwhitehorn * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21209878Snwhitehorn * 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 124571 2004-01-15 18:36:48Z jhb $");
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    /* Have to be in "just read it" mode */
89    if (f->f_flags != F_READ)
90	return(EPERM);
91
92    /* If the name already ends in `.split', ignore it */
93    if ((cp = strrchr(fname, '.')) && (!strcmp(cp, ".split")))
94	return(ENOENT);
95
96    /* Construct new name */
97    confname = malloc(strlen(fname) + 7);
98    sprintf(confname, "%s.split", fname);
99
100    /* Try to open the configuration file */
101    conffd = open(confname, O_RDONLY);
102    free(confname);
103    if (conffd == -1)
104	return(ENOENT);
105
106    if (fstat(conffd, &sb) < 0) {
107	printf("splitfs_open: stat failed\n");
108	close(conffd);
109	return(ENOENT);
110    }
111    if (!S_ISREG(sb.st_mode)) {
112	printf("splitfs_open: not a file\n");
113	close(conffd);
114	return(EISDIR);			/* best guess */
115    }
116
117    /* Allocate a split_file structure, populate it from the config file */
118    sf = malloc(sizeof(struct split_file));
119    bzero(sf, sizeof(struct split_file));
120    buf = malloc(CONF_BUF);
121    while (fgetstr(buf, CONF_BUF, conffd) > 0) {
122	cp = buf;
123	while ((*cp != '\0') && (isspace(*cp) == 0))
124	    cp++;
125	if (*cp != '\0') {
126	    *cp = '\0';
127	    cp++;
128	}
129	while ((*cp != '\0') && (isspace(*cp) != 0))
130	    cp++;
131	if (*cp == '\0')
132	    cp = buf;
133	sf->filesc++;
134	sf->filesv = realloc(sf->filesv, sizeof(*(sf->filesv)) * sf->filesc);
135	sf->descsv = realloc(sf->descsv, sizeof(*(sf->descsv)) * sf->filesc);
136	sf->filesv[sf->filesc - 1] = strdup(buf);
137	sf->descsv[sf->filesc - 1] = strdup(cp);
138    }
139    free(buf);
140    close(conffd);
141
142    if ((sf->filesc == 0) || ((sf->curfd = open(sf->filesv[0], O_RDONLY)) == -1)) {
143	split_file_destroy(sf);
144	return(ENOENT);
145    }
146
147    /* Looks OK, we'll take it */
148    f->f_fsdata = sf;
149    return (0);
150}
151
152static int
153splitfs_close(struct open_file *f)
154{
155    int fd;
156    struct split_file *sf;
157
158    sf = (struct split_file *)f->f_fsdata;
159    fd = sf->curfd;
160    split_file_destroy(sf);
161    return(close(fd));
162}
163
164static int
165splitfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
166{
167    int i, nread, totread;
168    struct split_file *sf;
169
170    sf = (struct split_file *)f->f_fsdata;
171    totread = 0;
172    do {
173	nread = read(sf->curfd, buf, size - totread);
174
175	/* Error? */
176	if (nread == -1)
177	    return (errno);
178
179	sf->tot_pos += nread;
180	sf->file_pos += nread;
181	totread += nread;
182	buf += nread;
183
184	if (totread < size) {				/* EOF */
185	    if (sf->curfile == (sf->filesc - 1))	/* Last slice */
186		break;
187
188	    /* Close previous slice */
189	    if (close(sf->curfd) != 0)
190		return (errno);
191
192	    sf->curfile++;
193	    for (i = 0;; i++) {
194		sf->curfd = open(sf->filesv[sf->curfile], O_RDONLY);
195		if (sf->curfd >= 0)
196		    break;
197		if ((sf->curfd == -1) && (errno != ENOENT))
198		    return (errno);
199		if (i == NTRIES)
200		    return (EIO);
201		printf("\nInsert disk labelled %s and press any key...", sf->descsv[sf->curfile]);
202		getchar();putchar('\n');
203	    }
204	    sf->file_pos = 0;
205	}
206    } while (totread < size);
207
208    if (resid != NULL)
209	*resid = size - totread;
210
211    return (0);
212}
213
214static off_t
215splitfs_seek(struct open_file *f, off_t offset, int where)
216{
217    int nread;
218    size_t resid;
219    off_t new_pos, seek_by;
220    struct split_file *sf;
221
222    sf = (struct split_file *)f->f_fsdata;
223
224    seek_by = offset;
225    switch (where) {
226    case SEEK_SET:
227	seek_by -= sf->tot_pos;
228	break;
229    case SEEK_CUR:
230	break;
231    case SEEK_END:
232	panic("splitfs_seek: SEEK_END not supported");
233	break;
234    }
235
236    if (seek_by > 0) {
237	/*
238	 * Seek forward - implemented using splitfs_read(), because otherwise we'll be
239	 * unable to detect that we have crossed slice boundary and hence
240	 * unable to do a long seek crossing that boundary.
241	 */
242	void *tmp;
243
244	tmp = malloc(SEEK_BUF);
245	if (tmp == NULL)
246	    return (-1);
247
248	nread = 0;
249	for (; seek_by > 0; seek_by -= nread) {
250	    resid = 0;
251	    errno = splitfs_read(f, tmp, min(seek_by, SEEK_BUF), &resid);
252	    nread = min(seek_by, SEEK_BUF) - resid;
253	    if ((errno != 0) || (nread == 0))
254		/* Error or EOF */
255		break;
256	}
257	free(tmp);
258	if (errno != 0)
259	    return (-1);
260    }
261
262    if (seek_by != 0) {
263	/* Seek backward or seek past the boundary of the last slice */
264	if (sf->file_pos + seek_by < 0)
265	    panic("splitfs_seek: can't seek past the beginning of the slice");
266	new_pos = lseek(sf->curfd, seek_by, SEEK_CUR);
267	if (new_pos < 0)
268	    return (-1);
269	sf->tot_pos += new_pos - sf->file_pos;
270	sf->file_pos = new_pos;
271    }
272
273    return (sf->tot_pos);
274}
275
276static int
277splitfs_stat(struct open_file *f, struct stat *sb)
278{
279    int	result;
280    struct split_file *sf = (struct split_file *)f->f_fsdata;
281
282    /* stat as normal, but indicate that size is unknown */
283    if ((result = fstat(sf->curfd, sb)) == 0)
284	sb->st_size = -1;
285    return (result);
286}
287