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: stable/11/stand/libsa/splitfs.c 329132 2018-02-11 19:51:29Z kevans $");
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    ssize_t nread;
196    size_t totread;
197    struct split_file *sf;
198
199    sf = (struct split_file *)f->f_fsdata;
200    totread = 0;
201    do {
202	nread = read(sf->curfd, buf, size - totread);
203
204	/* Error? */
205	if (nread == -1)
206	    return (errno);
207
208	sf->tot_pos += nread;
209	sf->file_pos += nread;
210	totread += nread;
211	buf = (char *)buf + nread;
212
213	if (totread < size) {				/* EOF */
214	    if (sf->curfile == (sf->filesc - 1))	/* Last slice */
215		break;
216
217	    /* Close previous slice */
218	    if (close(sf->curfd) != 0)
219		return (errno);
220
221	    sf->curfile++;
222	    errno = split_openfile(sf);
223	    if (errno)
224		    return (errno);
225	}
226    } while (totread < size);
227
228    if (resid != NULL)
229	*resid = size - totread;
230
231    return (0);
232}
233
234static off_t
235splitfs_seek(struct open_file *f, off_t offset, int where)
236{
237    int nread;
238    size_t resid;
239    off_t new_pos, seek_by;
240    struct split_file *sf;
241
242    sf = (struct split_file *)f->f_fsdata;
243
244    seek_by = offset;
245    switch (where) {
246    case SEEK_SET:
247	seek_by -= sf->tot_pos;
248	break;
249    case SEEK_CUR:
250	break;
251    case SEEK_END:
252	panic("splitfs_seek: SEEK_END not supported");
253	break;
254    default:
255	errno = EINVAL;
256	return (-1);
257    }
258
259    if (seek_by > 0) {
260	/*
261	 * Seek forward - implemented using splitfs_read(), because otherwise we'll be
262	 * unable to detect that we have crossed slice boundary and hence
263	 * unable to do a long seek crossing that boundary.
264	 */
265	void *tmp;
266
267	tmp = malloc(SEEK_BUF);
268	if (tmp == NULL) {
269	    errno = ENOMEM;
270	    return (-1);
271	}
272
273	nread = 0;
274	for (; seek_by > 0; seek_by -= nread) {
275	    resid = 0;
276	    errno = splitfs_read(f, tmp, min(seek_by, SEEK_BUF), &resid);
277	    nread = min(seek_by, SEEK_BUF) - resid;
278	    if ((errno != 0) || (nread == 0))
279		/* Error or EOF */
280		break;
281	}
282	free(tmp);
283	if (errno != 0)
284	    return (-1);
285    }
286
287    if (seek_by != 0) {
288	/* Seek backward or seek past the boundary of the last slice */
289	if (sf->file_pos + seek_by < 0)
290	    panic("splitfs_seek: can't seek past the beginning of the slice");
291	new_pos = lseek(sf->curfd, seek_by, SEEK_CUR);
292	if (new_pos < 0) {
293	    errno = EINVAL;
294	    return (-1);
295	}
296	sf->tot_pos += new_pos - sf->file_pos;
297	sf->file_pos = new_pos;
298    }
299
300    return (sf->tot_pos);
301}
302
303static int
304splitfs_stat(struct open_file *f, struct stat *sb)
305{
306    int	result;
307    struct split_file *sf = (struct split_file *)f->f_fsdata;
308
309    /* stat as normal, but indicate that size is unknown */
310    if ((result = fstat(sf->curfd, sb)) == 0)
311	sb->st_size = -1;
312    return (result);
313}
314