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