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