splitfs.c revision 136093
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 136093 2004-10-03 15:58:20Z stefanf $");
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	split_openfile(struct split_file *sf);
48static int	splitfs_open(const char *path, struct open_file *f);
49static int	splitfs_close(struct open_file *f);
50static int	splitfs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
51static off_t	splitfs_seek(struct open_file *f, off_t offset, int where);
52static int	splitfs_stat(struct open_file *f, struct stat *sb);
53
54struct fs_ops splitfs_fsops = {
55    "split",
56    splitfs_open,
57    splitfs_close,
58    splitfs_read,
59    null_write,
60    splitfs_seek,
61    splitfs_stat,
62    null_readdir
63};
64
65static void
66split_file_destroy(struct split_file *sf)
67{
68    int i;
69
70    if (sf->filesc > 0) {
71	for (i = 0; i < sf->filesc; i++) {
72	    free(sf->filesv[i]);
73	    free(sf->descsv[i]);
74	}
75	free(sf->filesv);
76	free(sf->descsv);
77    }
78    free(sf);
79}
80
81static int
82split_openfile(struct split_file *sf)
83{
84    int i;
85
86    for (i = 0;; i++) {
87	sf->curfd = open(sf->filesv[sf->curfile], O_RDONLY);
88	if (sf->curfd >= 0)
89	    break;
90	if ((sf->curfd == -1) && (errno != ENOENT))
91	    return (errno);
92	if (i == NTRIES)
93	    return (EIO);
94	printf("\nInsert disk labelled %s and press any key...",
95	    sf->descsv[sf->curfile]);
96	getchar();
97	putchar('\n');
98    }
99    sf->file_pos = 0;
100    return (0);
101}
102
103static int
104splitfs_open(const char *fname, struct open_file *f)
105{
106    char *buf, *confname, *cp;
107    int	conffd;
108    struct split_file *sf;
109    struct stat sb;
110
111    /* Have to be in "just read it" mode */
112    if (f->f_flags != F_READ)
113	return(EPERM);
114
115    /* If the name already ends in `.split', ignore it */
116    if ((cp = strrchr(fname, '.')) && (!strcmp(cp, ".split")))
117	return(ENOENT);
118
119    /* Construct new name */
120    confname = malloc(strlen(fname) + 7);
121    sprintf(confname, "%s.split", fname);
122
123    /* Try to open the configuration file */
124    conffd = open(confname, O_RDONLY);
125    free(confname);
126    if (conffd == -1)
127	return(ENOENT);
128
129    if (fstat(conffd, &sb) < 0) {
130	printf("splitfs_open: stat failed\n");
131	close(conffd);
132	return(ENOENT);
133    }
134    if (!S_ISREG(sb.st_mode)) {
135	printf("splitfs_open: not a file\n");
136	close(conffd);
137	return(EISDIR);			/* best guess */
138    }
139
140    /* Allocate a split_file structure, populate it from the config file */
141    sf = malloc(sizeof(struct split_file));
142    bzero(sf, sizeof(struct split_file));
143    buf = malloc(CONF_BUF);
144    while (fgetstr(buf, CONF_BUF, conffd) > 0) {
145	cp = buf;
146	while ((*cp != '\0') && (isspace(*cp) == 0))
147	    cp++;
148	if (*cp != '\0') {
149	    *cp = '\0';
150	    cp++;
151	}
152	while ((*cp != '\0') && (isspace(*cp) != 0))
153	    cp++;
154	if (*cp == '\0')
155	    cp = buf;
156	sf->filesc++;
157	sf->filesv = realloc(sf->filesv, sizeof(*(sf->filesv)) * sf->filesc);
158	sf->descsv = realloc(sf->descsv, sizeof(*(sf->descsv)) * sf->filesc);
159	sf->filesv[sf->filesc - 1] = strdup(buf);
160	sf->descsv[sf->filesc - 1] = strdup(cp);
161    }
162    free(buf);
163    close(conffd);
164
165    if (sf->filesc == 0) {
166	split_file_destroy(sf);
167	return(ENOENT);
168    }
169    errno = split_openfile(sf);
170    if (errno != 0) {
171	split_file_destroy(sf);
172	return(ENOENT);
173    }
174
175    /* Looks OK, we'll take it */
176    f->f_fsdata = sf;
177    return (0);
178}
179
180static int
181splitfs_close(struct open_file *f)
182{
183    int fd;
184    struct split_file *sf;
185
186    sf = (struct split_file *)f->f_fsdata;
187    fd = sf->curfd;
188    split_file_destroy(sf);
189    return(close(fd));
190}
191
192static int
193splitfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
194{
195    int i, nread, totread;
196    struct split_file *sf;
197
198    sf = (struct split_file *)f->f_fsdata;
199    totread = 0;
200    do {
201	nread = read(sf->curfd, buf, size - totread);
202
203	/* Error? */
204	if (nread == -1)
205	    return (errno);
206
207	sf->tot_pos += nread;
208	sf->file_pos += nread;
209	totread += nread;
210	buf = (char *)buf + nread;
211
212	if (totread < size) {				/* EOF */
213	    if (sf->curfile == (sf->filesc - 1))	/* Last slice */
214		break;
215
216	    /* Close previous slice */
217	    if (close(sf->curfd) != 0)
218		return (errno);
219
220	    sf->curfile++;
221	    errno = split_openfile(sf);
222	    if (errno)
223		    return (errno);
224	}
225    } while (totread < size);
226
227    if (resid != NULL)
228	*resid = size - totread;
229
230    return (0);
231}
232
233static off_t
234splitfs_seek(struct open_file *f, off_t offset, int where)
235{
236    int nread;
237    size_t resid;
238    off_t new_pos, seek_by;
239    struct split_file *sf;
240
241    sf = (struct split_file *)f->f_fsdata;
242
243    seek_by = offset;
244    switch (where) {
245    case SEEK_SET:
246	seek_by -= sf->tot_pos;
247	break;
248    case SEEK_CUR:
249	break;
250    case SEEK_END:
251	panic("splitfs_seek: SEEK_END not supported");
252	break;
253    default:
254	errno = EINVAL;
255	return (-1);
256    }
257
258    if (seek_by > 0) {
259	/*
260	 * Seek forward - implemented using splitfs_read(), because otherwise we'll be
261	 * unable to detect that we have crossed slice boundary and hence
262	 * unable to do a long seek crossing that boundary.
263	 */
264	void *tmp;
265
266	tmp = malloc(SEEK_BUF);
267	if (tmp == NULL) {
268	    errno = ENOMEM;
269	    return (-1);
270	}
271
272	nread = 0;
273	for (; seek_by > 0; seek_by -= nread) {
274	    resid = 0;
275	    errno = splitfs_read(f, tmp, min(seek_by, SEEK_BUF), &resid);
276	    nread = min(seek_by, SEEK_BUF) - resid;
277	    if ((errno != 0) || (nread == 0))
278		/* Error or EOF */
279		break;
280	}
281	free(tmp);
282	if (errno != 0)
283	    return (-1);
284    }
285
286    if (seek_by != 0) {
287	/* Seek backward or seek past the boundary of the last slice */
288	if (sf->file_pos + seek_by < 0)
289	    panic("splitfs_seek: can't seek past the beginning of the slice");
290	new_pos = lseek(sf->curfd, seek_by, SEEK_CUR);
291	if (new_pos < 0) {
292	    errno = EINVAL;
293	    return (-1);
294	}
295	sf->tot_pos += new_pos - sf->file_pos;
296	sf->file_pos = new_pos;
297    }
298
299    return (sf->tot_pos);
300}
301
302static int
303splitfs_stat(struct open_file *f, struct stat *sb)
304{
305    int	result;
306    struct split_file *sf = (struct split_file *)f->f_fsdata;
307
308    /* stat as normal, but indicate that size is unknown */
309    if ((result = fstat(sf->curfd, sb)) == 0)
310	sb->st_size = -1;
311    return (result);
312}
313