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