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