xref: /illumos-gate/usr/src/boot/libsa/splitfs.c (revision 55fea89d)
1*55fea89dSDan Cross /*
2199767f8SToomas Soome  * Copyright (c) 2002 Maxim Sobolev
3199767f8SToomas Soome  * All rights reserved.
4199767f8SToomas Soome  *
5199767f8SToomas Soome  * Redistribution and use in source and binary forms, with or without
6199767f8SToomas Soome  * modification, are permitted provided that the following conditions
7199767f8SToomas Soome  * are met:
8199767f8SToomas Soome  * 1. Redistributions of source code must retain the above copyright
9199767f8SToomas Soome  *    notice, this list of conditions and the following disclaimer.
10199767f8SToomas Soome  * 2. Redistributions in binary form must reproduce the above copyright
11199767f8SToomas Soome  *    notice, this list of conditions and the following disclaimer in the
12199767f8SToomas Soome  *    documentation and/or other materials provided with the distribution.
13199767f8SToomas Soome  *
14199767f8SToomas Soome  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15199767f8SToomas Soome  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16199767f8SToomas Soome  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17199767f8SToomas Soome  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18199767f8SToomas Soome  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19199767f8SToomas Soome  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20199767f8SToomas Soome  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21199767f8SToomas Soome  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22199767f8SToomas Soome  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23199767f8SToomas Soome  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24199767f8SToomas Soome  * SUCH DAMAGE.
25199767f8SToomas Soome  */
26199767f8SToomas Soome 
27199767f8SToomas Soome #include <sys/cdefs.h>
28199767f8SToomas Soome __FBSDID("$FreeBSD$");
29199767f8SToomas Soome 
30199767f8SToomas Soome #include "stand.h"
31199767f8SToomas Soome 
32199767f8SToomas Soome #define NTRIES		(3)
33199767f8SToomas Soome #define CONF_BUF	(512)
34199767f8SToomas Soome #define SEEK_BUF	(512)
35199767f8SToomas Soome 
36199767f8SToomas Soome struct split_file
37199767f8SToomas Soome {
38199767f8SToomas Soome     char  **filesv;	/* Filenames */
39199767f8SToomas Soome     char  **descsv;	/* Descriptions */
40199767f8SToomas Soome     int	  filesc;	/* Number of parts */
41199767f8SToomas Soome     int	  curfile;	/* Current file number */
42199767f8SToomas Soome     int	  curfd;	/* Current file descriptor */
43199767f8SToomas Soome     off_t tot_pos;	/* Offset from the beginning of the sequence */
44199767f8SToomas Soome     off_t file_pos;	/* Offset from the beginning of the slice */
45199767f8SToomas Soome };
46199767f8SToomas Soome 
47199767f8SToomas Soome static int	split_openfile(struct split_file *sf);
48199767f8SToomas Soome static int	splitfs_open(const char *path, struct open_file *f);
49199767f8SToomas Soome static int	splitfs_close(struct open_file *f);
50199767f8SToomas Soome static int	splitfs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
51199767f8SToomas Soome static off_t	splitfs_seek(struct open_file *f, off_t offset, int where);
52199767f8SToomas Soome static int	splitfs_stat(struct open_file *f, struct stat *sb);
53199767f8SToomas Soome 
54199767f8SToomas Soome struct fs_ops splitfs_fsops = {
55199767f8SToomas Soome     "split",
56*55fea89dSDan Cross     splitfs_open,
57*55fea89dSDan Cross     splitfs_close,
58199767f8SToomas Soome     splitfs_read,
59199767f8SToomas Soome     null_write,
60199767f8SToomas Soome     splitfs_seek,
61199767f8SToomas Soome     splitfs_stat,
62199767f8SToomas Soome     null_readdir
63199767f8SToomas Soome };
64199767f8SToomas Soome 
65199767f8SToomas Soome static void
split_file_destroy(struct split_file * sf)66199767f8SToomas Soome split_file_destroy(struct split_file *sf)
67199767f8SToomas Soome {
68199767f8SToomas Soome     int i;
69199767f8SToomas Soome 
70199767f8SToomas Soome     if (sf->filesc > 0) {
71199767f8SToomas Soome 	for (i = 0; i < sf->filesc; i++) {
72199767f8SToomas Soome 	    free(sf->filesv[i]);
73199767f8SToomas Soome 	    free(sf->descsv[i]);
74199767f8SToomas Soome 	}
75199767f8SToomas Soome 	free(sf->filesv);
76199767f8SToomas Soome 	free(sf->descsv);
77199767f8SToomas Soome     }
78199767f8SToomas Soome     free(sf);
79199767f8SToomas Soome }
80199767f8SToomas Soome 
81199767f8SToomas Soome static int
split_openfile(struct split_file * sf)82199767f8SToomas Soome split_openfile(struct split_file *sf)
83199767f8SToomas Soome {
84199767f8SToomas Soome     int i;
85199767f8SToomas Soome 
86199767f8SToomas Soome     for (i = 0;; i++) {
87199767f8SToomas Soome 	sf->curfd = open(sf->filesv[sf->curfile], O_RDONLY);
88199767f8SToomas Soome 	if (sf->curfd >= 0)
89199767f8SToomas Soome 	    break;
90199767f8SToomas Soome 	if ((sf->curfd == -1) && (errno != ENOENT))
91199767f8SToomas Soome 	    return (errno);
92199767f8SToomas Soome 	if (i == NTRIES)
93199767f8SToomas Soome 	    return (EIO);
94199767f8SToomas Soome 	printf("\nInsert disk labelled %s and press any key...",
95199767f8SToomas Soome 	    sf->descsv[sf->curfile]);
96199767f8SToomas Soome 	getchar();
97199767f8SToomas Soome 	putchar('\n');
98199767f8SToomas Soome     }
99199767f8SToomas Soome     sf->file_pos = 0;
100199767f8SToomas Soome     return (0);
101199767f8SToomas Soome }
102199767f8SToomas Soome 
103199767f8SToomas Soome static int
splitfs_open(const char * fname,struct open_file * f)104199767f8SToomas Soome splitfs_open(const char *fname, struct open_file *f)
105199767f8SToomas Soome {
106199767f8SToomas Soome     char *buf, *confname, *cp;
107199767f8SToomas Soome     int	conffd;
108199767f8SToomas Soome     struct split_file *sf;
109199767f8SToomas Soome     struct stat sb;
110199767f8SToomas Soome 
111199767f8SToomas Soome     /* Have to be in "just read it" mode */
112199767f8SToomas Soome     if (f->f_flags != F_READ)
113199767f8SToomas Soome 	return(EPERM);
114199767f8SToomas Soome 
115199767f8SToomas Soome     /* If the name already ends in `.split', ignore it */
116199767f8SToomas Soome     if ((cp = strrchr(fname, '.')) && (!strcmp(cp, ".split")))
117199767f8SToomas Soome 	return(ENOENT);
118199767f8SToomas Soome 
119199767f8SToomas Soome     /* Construct new name */
120199767f8SToomas Soome     confname = malloc(strlen(fname) + 7);
121199767f8SToomas Soome     sprintf(confname, "%s.split", fname);
122199767f8SToomas Soome 
123199767f8SToomas Soome     /* Try to open the configuration file */
124199767f8SToomas Soome     conffd = open(confname, O_RDONLY);
125199767f8SToomas Soome     free(confname);
126199767f8SToomas Soome     if (conffd == -1)
127199767f8SToomas Soome 	return(ENOENT);
128199767f8SToomas Soome 
129199767f8SToomas Soome     if (fstat(conffd, &sb) < 0) {
130199767f8SToomas Soome 	printf("splitfs_open: stat failed\n");
131199767f8SToomas Soome 	close(conffd);
132199767f8SToomas Soome 	return(ENOENT);
133199767f8SToomas Soome     }
134199767f8SToomas Soome     if (!S_ISREG(sb.st_mode)) {
135199767f8SToomas Soome 	printf("splitfs_open: not a file\n");
136199767f8SToomas Soome 	close(conffd);
137199767f8SToomas Soome 	return(EISDIR);			/* best guess */
138199767f8SToomas Soome     }
139199767f8SToomas Soome 
140199767f8SToomas Soome     /* Allocate a split_file structure, populate it from the config file */
141199767f8SToomas Soome     sf = malloc(sizeof(struct split_file));
142199767f8SToomas Soome     bzero(sf, sizeof(struct split_file));
143199767f8SToomas Soome     buf = malloc(CONF_BUF);
144199767f8SToomas Soome     while (fgetstr(buf, CONF_BUF, conffd) > 0) {
145199767f8SToomas Soome 	cp = buf;
146199767f8SToomas Soome 	while ((*cp != '\0') && (isspace(*cp) == 0))
147199767f8SToomas Soome 	    cp++;
148199767f8SToomas Soome 	if (*cp != '\0') {
149199767f8SToomas Soome 	    *cp = '\0';
150199767f8SToomas Soome 	    cp++;
151199767f8SToomas Soome 	}
152199767f8SToomas Soome 	while ((*cp != '\0') && (isspace(*cp) != 0))
153199767f8SToomas Soome 	    cp++;
154199767f8SToomas Soome 	if (*cp == '\0')
155199767f8SToomas Soome 	    cp = buf;
156199767f8SToomas Soome 	sf->filesc++;
157199767f8SToomas Soome 	sf->filesv = realloc(sf->filesv, sizeof(*(sf->filesv)) * sf->filesc);
158199767f8SToomas Soome 	sf->descsv = realloc(sf->descsv, sizeof(*(sf->descsv)) * sf->filesc);
159199767f8SToomas Soome 	sf->filesv[sf->filesc - 1] = strdup(buf);
160199767f8SToomas Soome 	sf->descsv[sf->filesc - 1] = strdup(cp);
161199767f8SToomas Soome     }
162199767f8SToomas Soome     free(buf);
163199767f8SToomas Soome     close(conffd);
164199767f8SToomas Soome 
165199767f8SToomas Soome     if (sf->filesc == 0) {
166199767f8SToomas Soome 	split_file_destroy(sf);
167199767f8SToomas Soome 	return(ENOENT);
168199767f8SToomas Soome     }
169199767f8SToomas Soome     errno = split_openfile(sf);
170199767f8SToomas Soome     if (errno != 0) {
171199767f8SToomas Soome 	split_file_destroy(sf);
172199767f8SToomas Soome 	return(ENOENT);
173199767f8SToomas Soome     }
174199767f8SToomas Soome 
175199767f8SToomas Soome     /* Looks OK, we'll take it */
176199767f8SToomas Soome     f->f_fsdata = sf;
177199767f8SToomas Soome     return (0);
178199767f8SToomas Soome }
179199767f8SToomas Soome 
180199767f8SToomas Soome static int
splitfs_close(struct open_file * f)181199767f8SToomas Soome splitfs_close(struct open_file *f)
182199767f8SToomas Soome {
183199767f8SToomas Soome     int fd;
184199767f8SToomas Soome     struct split_file *sf;
185199767f8SToomas Soome 
186199767f8SToomas Soome     sf = (struct split_file *)f->f_fsdata;
187199767f8SToomas Soome     fd = sf->curfd;
188199767f8SToomas Soome     split_file_destroy(sf);
189199767f8SToomas Soome     return(close(fd));
190199767f8SToomas Soome }
191*55fea89dSDan Cross 
192*55fea89dSDan Cross static int
splitfs_read(struct open_file * f,void * buf,size_t size,size_t * resid)193199767f8SToomas Soome splitfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
194199767f8SToomas Soome {
195199767f8SToomas Soome     ssize_t nread;
196199767f8SToomas Soome     size_t totread;
197199767f8SToomas Soome     struct split_file *sf;
198199767f8SToomas Soome 
199199767f8SToomas Soome     sf = (struct split_file *)f->f_fsdata;
200199767f8SToomas Soome     totread = 0;
201199767f8SToomas Soome     do {
202199767f8SToomas Soome 	nread = read(sf->curfd, buf, size - totread);
203199767f8SToomas Soome 
204199767f8SToomas Soome 	/* Error? */
205199767f8SToomas Soome 	if (nread == -1)
206199767f8SToomas Soome 	    return (errno);
207199767f8SToomas Soome 
208199767f8SToomas Soome 	sf->tot_pos += nread;
209199767f8SToomas Soome 	sf->file_pos += nread;
210199767f8SToomas Soome 	totread += nread;
211199767f8SToomas Soome 	buf = (char *)buf + nread;
212199767f8SToomas Soome 
213199767f8SToomas Soome 	if (totread < size) {				/* EOF */
214199767f8SToomas Soome 	    if (sf->curfile == (sf->filesc - 1))	/* Last slice */
215199767f8SToomas Soome 		break;
216199767f8SToomas Soome 
217199767f8SToomas Soome 	    /* Close previous slice */
218199767f8SToomas Soome 	    if (close(sf->curfd) != 0)
219199767f8SToomas Soome 		return (errno);
220199767f8SToomas Soome 
221199767f8SToomas Soome 	    sf->curfile++;
222199767f8SToomas Soome 	    errno = split_openfile(sf);
223199767f8SToomas Soome 	    if (errno)
224199767f8SToomas Soome 		    return (errno);
225199767f8SToomas Soome 	}
226199767f8SToomas Soome     } while (totread < size);
227199767f8SToomas Soome 
228199767f8SToomas Soome     if (resid != NULL)
229199767f8SToomas Soome 	*resid = size - totread;
230199767f8SToomas Soome 
231199767f8SToomas Soome     return (0);
232199767f8SToomas Soome }
233199767f8SToomas Soome 
234199767f8SToomas Soome static off_t
splitfs_seek(struct open_file * f,off_t offset,int where)235199767f8SToomas Soome splitfs_seek(struct open_file *f, off_t offset, int where)
236199767f8SToomas Soome {
237199767f8SToomas Soome     int nread;
238199767f8SToomas Soome     size_t resid;
239199767f8SToomas Soome     off_t new_pos, seek_by;
240199767f8SToomas Soome     struct split_file *sf;
241199767f8SToomas Soome 
242199767f8SToomas Soome     sf = (struct split_file *)f->f_fsdata;
243199767f8SToomas Soome 
244199767f8SToomas Soome     seek_by = offset;
245199767f8SToomas Soome     switch (where) {
246199767f8SToomas Soome     case SEEK_SET:
247199767f8SToomas Soome 	seek_by -= sf->tot_pos;
248199767f8SToomas Soome 	break;
249199767f8SToomas Soome     case SEEK_CUR:
250199767f8SToomas Soome 	break;
251199767f8SToomas Soome     case SEEK_END:
252199767f8SToomas Soome 	panic("splitfs_seek: SEEK_END not supported");
253199767f8SToomas Soome 	break;
254199767f8SToomas Soome     default:
255199767f8SToomas Soome 	errno = EINVAL;
256199767f8SToomas Soome 	return (-1);
257199767f8SToomas Soome     }
258199767f8SToomas Soome 
259199767f8SToomas Soome     if (seek_by > 0) {
260199767f8SToomas Soome 	/*
261199767f8SToomas Soome 	 * Seek forward - implemented using splitfs_read(), because otherwise we'll be
262199767f8SToomas Soome 	 * unable to detect that we have crossed slice boundary and hence
263199767f8SToomas Soome 	 * unable to do a long seek crossing that boundary.
264199767f8SToomas Soome 	 */
265199767f8SToomas Soome 	void *tmp;
266199767f8SToomas Soome 
267199767f8SToomas Soome 	tmp = malloc(SEEK_BUF);
268199767f8SToomas Soome 	if (tmp == NULL) {
269199767f8SToomas Soome 	    errno = ENOMEM;
270199767f8SToomas Soome 	    return (-1);
271199767f8SToomas Soome 	}
272199767f8SToomas Soome 
273199767f8SToomas Soome 	nread = 0;
274199767f8SToomas Soome 	for (; seek_by > 0; seek_by -= nread) {
275199767f8SToomas Soome 	    resid = 0;
276199767f8SToomas Soome 	    errno = splitfs_read(f, tmp, min(seek_by, SEEK_BUF), &resid);
277199767f8SToomas Soome 	    nread = min(seek_by, SEEK_BUF) - resid;
278199767f8SToomas Soome 	    if ((errno != 0) || (nread == 0))
279199767f8SToomas Soome 		/* Error or EOF */
280199767f8SToomas Soome 		break;
281199767f8SToomas Soome 	}
282199767f8SToomas Soome 	free(tmp);
283199767f8SToomas Soome 	if (errno != 0)
284199767f8SToomas Soome 	    return (-1);
285199767f8SToomas Soome     }
286199767f8SToomas Soome 
287199767f8SToomas Soome     if (seek_by != 0) {
288199767f8SToomas Soome 	/* Seek backward or seek past the boundary of the last slice */
289199767f8SToomas Soome 	if (sf->file_pos + seek_by < 0)
290199767f8SToomas Soome 	    panic("splitfs_seek: can't seek past the beginning of the slice");
291199767f8SToomas Soome 	new_pos = lseek(sf->curfd, seek_by, SEEK_CUR);
292199767f8SToomas Soome 	if (new_pos < 0) {
293199767f8SToomas Soome 	    errno = EINVAL;
294199767f8SToomas Soome 	    return (-1);
295199767f8SToomas Soome 	}
296199767f8SToomas Soome 	sf->tot_pos += new_pos - sf->file_pos;
297199767f8SToomas Soome 	sf->file_pos = new_pos;
298199767f8SToomas Soome     }
299199767f8SToomas Soome 
300199767f8SToomas Soome     return (sf->tot_pos);
301199767f8SToomas Soome }
302199767f8SToomas Soome 
303199767f8SToomas Soome static int
splitfs_stat(struct open_file * f,struct stat * sb)304199767f8SToomas Soome splitfs_stat(struct open_file *f, struct stat *sb)
305199767f8SToomas Soome {
306199767f8SToomas Soome     int	result;
307199767f8SToomas Soome     struct split_file *sf = (struct split_file *)f->f_fsdata;
308199767f8SToomas Soome 
309199767f8SToomas Soome     /* stat as normal, but indicate that size is unknown */
310199767f8SToomas Soome     if ((result = fstat(sf->curfd, sb)) == 0)
311199767f8SToomas Soome 	sb->st_size = -1;
312199767f8SToomas Soome     return (result);
313199767f8SToomas Soome }
314