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 "stand.h"
28
29 #define NTRIES (3)
30 #define CONF_BUF (512)
31 #define SEEK_BUF (512)
32
33 struct split_file
34 {
35 char **filesv; /* Filenames */
36 char **descsv; /* Descriptions */
37 int filesc; /* Number of parts */
38 int curfile; /* Current file number */
39 int curfd; /* Current file descriptor */
40 off_t tot_pos; /* Offset from the beginning of the sequence */
41 off_t file_pos; /* Offset from the beginning of the slice */
42 };
43
44 static int split_openfile(struct split_file *sf);
45 static int splitfs_open(const char *path, struct open_file *f);
46 static int splitfs_close(struct open_file *f);
47 static int splitfs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
48 static off_t splitfs_seek(struct open_file *f, off_t offset, int where);
49 static int splitfs_stat(struct open_file *f, struct stat *sb);
50
51 struct fs_ops splitfs_fsops = {
52 .fs_name = "split",
53 .fs_flags = 0,
54 .fo_open = splitfs_open,
55 .fo_close = splitfs_close,
56 .fo_read = splitfs_read,
57 .fo_write = null_write,
58 .fo_seek = splitfs_seek,
59 .fo_stat = splitfs_stat,
60 .fo_readdir = null_readdir,
61 };
62
63 static void
split_file_destroy(struct split_file * sf)64 split_file_destroy(struct split_file *sf)
65 {
66 int i;
67
68 if (sf->filesc > 0) {
69 for (i = 0; i < sf->filesc; i++) {
70 free(sf->filesv[i]);
71 free(sf->descsv[i]);
72 }
73 free(sf->filesv);
74 free(sf->descsv);
75 }
76 free(sf);
77 }
78
79 static int
split_openfile(struct split_file * sf)80 split_openfile(struct split_file *sf)
81 {
82 int i;
83
84 for (i = 0;; i++) {
85 sf->curfd = open(sf->filesv[sf->curfile], O_RDONLY);
86 if (sf->curfd >= 0)
87 break;
88 if ((sf->curfd == -1) && (errno != ENOENT))
89 return (errno);
90 if (i == NTRIES)
91 return (EIO);
92 printf("\nInsert disk labelled %s and press any key...",
93 sf->descsv[sf->curfile]);
94 getchar();
95 putchar('\n');
96 }
97 sf->file_pos = 0;
98 return (0);
99 }
100
101 static int
splitfs_open(const char * fname,struct open_file * f)102 splitfs_open(const char *fname, struct open_file *f)
103 {
104 char *buf, *confname, *cp;
105 int conffd;
106 struct split_file *sf;
107 struct stat sb;
108
109 /* Have to be in "just read it" mode */
110 if (f->f_flags != F_READ)
111 return(EPERM);
112
113 /* If the name already ends in `.split', ignore it */
114 if ((cp = strrchr(fname, '.')) && (!strcmp(cp, ".split")))
115 return(ENOENT);
116
117 /* Construct new name */
118 confname = malloc(strlen(fname) + 7);
119 sprintf(confname, "%s.split", fname);
120
121 /* Try to open the configuration file */
122 conffd = open(confname, O_RDONLY);
123 free(confname);
124 if (conffd == -1)
125 return(ENOENT);
126
127 if (fstat(conffd, &sb) < 0) {
128 printf("splitfs_open: stat failed\n");
129 close(conffd);
130 return(ENOENT);
131 }
132 if (!S_ISREG(sb.st_mode)) {
133 printf("splitfs_open: not a file\n");
134 close(conffd);
135 return(EISDIR); /* best guess */
136 }
137
138 /* Allocate a split_file structure, populate it from the config file */
139 sf = malloc(sizeof(struct split_file));
140 bzero(sf, sizeof(struct split_file));
141 buf = malloc(CONF_BUF);
142 while (fgetstr(buf, CONF_BUF, conffd) > 0) {
143 cp = buf;
144 while ((*cp != '\0') && (isspace(*cp) == 0))
145 cp++;
146 if (*cp != '\0') {
147 *cp = '\0';
148 cp++;
149 }
150 while ((*cp != '\0') && (isspace(*cp) != 0))
151 cp++;
152 if (*cp == '\0')
153 cp = buf;
154 sf->filesc++;
155 sf->filesv = realloc(sf->filesv, sizeof(*(sf->filesv)) * sf->filesc);
156 sf->descsv = realloc(sf->descsv, sizeof(*(sf->descsv)) * sf->filesc);
157 sf->filesv[sf->filesc - 1] = strdup(buf);
158 sf->descsv[sf->filesc - 1] = strdup(cp);
159 }
160 free(buf);
161 close(conffd);
162
163 if (sf->filesc == 0) {
164 split_file_destroy(sf);
165 return(ENOENT);
166 }
167 errno = split_openfile(sf);
168 if (errno != 0) {
169 split_file_destroy(sf);
170 return(ENOENT);
171 }
172
173 /* Looks OK, we'll take it */
174 f->f_fsdata = sf;
175 return (0);
176 }
177
178 static int
splitfs_close(struct open_file * f)179 splitfs_close(struct open_file *f)
180 {
181 int fd;
182 struct split_file *sf;
183
184 sf = (struct split_file *)f->f_fsdata;
185 fd = sf->curfd;
186 split_file_destroy(sf);
187 return(close(fd));
188 }
189
190 static int
splitfs_read(struct open_file * f,void * buf,size_t size,size_t * resid)191 splitfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
192 {
193 ssize_t nread;
194 size_t totread;
195 struct split_file *sf;
196
197 sf = (struct split_file *)f->f_fsdata;
198 totread = 0;
199 do {
200 nread = read(sf->curfd, buf, size - totread);
201
202 /* Error? */
203 if (nread == -1)
204 return (errno);
205
206 sf->tot_pos += nread;
207 sf->file_pos += nread;
208 totread += nread;
209 buf = (char *)buf + nread;
210
211 if (totread < size) { /* EOF */
212 if (sf->curfile == (sf->filesc - 1)) /* Last slice */
213 break;
214
215 /* Close previous slice */
216 if (close(sf->curfd) != 0)
217 return (errno);
218
219 sf->curfile++;
220 errno = split_openfile(sf);
221 if (errno)
222 return (errno);
223 }
224 } while (totread < size);
225
226 if (resid != NULL)
227 *resid = size - totread;
228
229 return (0);
230 }
231
232 static off_t
splitfs_seek(struct open_file * f,off_t offset,int where)233 splitfs_seek(struct open_file *f, off_t offset, int where)
234 {
235 int nread;
236 size_t resid;
237 off_t new_pos, seek_by;
238 struct split_file *sf;
239
240 sf = (struct split_file *)f->f_fsdata;
241
242 seek_by = offset;
243 switch (where) {
244 case SEEK_SET:
245 seek_by -= sf->tot_pos;
246 break;
247 case SEEK_CUR:
248 break;
249 case SEEK_END:
250 panic("splitfs_seek: SEEK_END not supported");
251 break;
252 default:
253 errno = EINVAL;
254 return (-1);
255 }
256
257 if (seek_by > 0) {
258 /*
259 * Seek forward - implemented using splitfs_read(), because otherwise we'll be
260 * unable to detect that we have crossed slice boundary and hence
261 * unable to do a long seek crossing that boundary.
262 */
263 void *tmp;
264
265 tmp = malloc(SEEK_BUF);
266 if (tmp == NULL) {
267 errno = ENOMEM;
268 return (-1);
269 }
270
271 nread = 0;
272 for (; seek_by > 0; seek_by -= nread) {
273 resid = 0;
274 errno = splitfs_read(f, tmp, min(seek_by, SEEK_BUF), &resid);
275 nread = min(seek_by, SEEK_BUF) - resid;
276 if ((errno != 0) || (nread == 0))
277 /* Error or EOF */
278 break;
279 }
280 free(tmp);
281 if (errno != 0)
282 return (-1);
283 }
284
285 if (seek_by != 0) {
286 /* Seek backward or seek past the boundary of the last slice */
287 if (sf->file_pos + seek_by < 0)
288 panic("splitfs_seek: can't seek past the beginning of the slice");
289 new_pos = lseek(sf->curfd, seek_by, SEEK_CUR);
290 if (new_pos < 0) {
291 errno = EINVAL;
292 return (-1);
293 }
294 sf->tot_pos += new_pos - sf->file_pos;
295 sf->file_pos = new_pos;
296 }
297
298 return (sf->tot_pos);
299 }
300
301 static int
splitfs_stat(struct open_file * f,struct stat * sb)302 splitfs_stat(struct open_file *f, struct stat *sb)
303 {
304 int result;
305 struct split_file *sf = (struct split_file *)f->f_fsdata;
306
307 /* stat as normal, but indicate that size is unknown */
308 if ((result = fstat(sf->curfd, sb)) == 0)
309 sb->st_size = -1;
310 return (result);
311 }
312