8010 loader: want mechanism to avoid RA with bcache
[unleashed.git] / usr / src / boot / lib / libstand / splitfs.c
blobaf28704bc465834f17695fd10914432f67e7c453
1 /*
2 * Copyright (c) 2002 Maxim Sobolev
3 * All rights reserved.
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.
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.
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
30 #include "stand.h"
32 #define NTRIES (3)
33 #define CONF_BUF (512)
34 #define SEEK_BUF (512)
36 struct split_file
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 */
47 static int split_openfile(struct split_file *sf);
48 static int splitfs_open(const char *path, struct open_file *f);
49 static int splitfs_close(struct open_file *f);
50 static int splitfs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
51 static off_t splitfs_seek(struct open_file *f, off_t offset, int where);
52 static int splitfs_stat(struct open_file *f, struct stat *sb);
54 struct 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
65 static void
66 split_file_destroy(struct split_file *sf)
68 int i;
70 if (sf->filesc > 0) {
71 for (i = 0; i < sf->filesc; i++) {
72 free(sf->filesv[i]);
73 free(sf->descsv[i]);
75 free(sf->filesv);
76 free(sf->descsv);
78 free(sf);
81 static int
82 split_openfile(struct split_file *sf)
84 int i;
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');
99 sf->file_pos = 0;
100 return (0);
103 static int
104 splitfs_open(const char *fname, struct open_file *f)
106 char *buf, *confname, *cp;
107 int conffd;
108 struct split_file *sf;
109 struct stat sb;
111 /* Have to be in "just read it" mode */
112 if (f->f_flags != F_READ)
113 return(EPERM);
115 /* If the name already ends in `.split', ignore it */
116 if ((cp = strrchr(fname, '.')) && (!strcmp(cp, ".split")))
117 return(ENOENT);
119 /* Construct new name */
120 confname = malloc(strlen(fname) + 7);
121 sprintf(confname, "%s.split", fname);
123 /* Try to open the configuration file */
124 conffd = open(confname, O_RDONLY);
125 free(confname);
126 if (conffd == -1)
127 return(ENOENT);
129 if (fstat(conffd, &sb) < 0) {
130 printf("splitfs_open: stat failed\n");
131 close(conffd);
132 return(ENOENT);
134 if (!S_ISREG(sb.st_mode)) {
135 printf("splitfs_open: not a file\n");
136 close(conffd);
137 return(EISDIR); /* best guess */
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++;
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);
162 free(buf);
163 close(conffd);
165 if (sf->filesc == 0) {
166 split_file_destroy(sf);
167 return(ENOENT);
169 errno = split_openfile(sf);
170 if (errno != 0) {
171 split_file_destroy(sf);
172 return(ENOENT);
175 /* Looks OK, we'll take it */
176 f->f_fsdata = sf;
177 return (0);
180 static int
181 splitfs_close(struct open_file *f)
183 int fd;
184 struct split_file *sf;
186 sf = (struct split_file *)f->f_fsdata;
187 fd = sf->curfd;
188 split_file_destroy(sf);
189 return(close(fd));
192 static int
193 splitfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
195 ssize_t nread;
196 size_t totread;
197 struct split_file *sf;
199 sf = (struct split_file *)f->f_fsdata;
200 totread = 0;
201 do {
202 nread = read(sf->curfd, buf, size - totread);
204 /* Error? */
205 if (nread == -1)
206 return (errno);
208 sf->tot_pos += nread;
209 sf->file_pos += nread;
210 totread += nread;
211 buf = (char *)buf + nread;
213 if (totread < size) { /* EOF */
214 if (sf->curfile == (sf->filesc - 1)) /* Last slice */
215 break;
217 /* Close previous slice */
218 if (close(sf->curfd) != 0)
219 return (errno);
221 sf->curfile++;
222 errno = split_openfile(sf);
223 if (errno)
224 return (errno);
226 } while (totread < size);
228 if (resid != NULL)
229 *resid = size - totread;
231 return (0);
234 static off_t
235 splitfs_seek(struct open_file *f, off_t offset, int where)
237 int nread;
238 size_t resid;
239 off_t new_pos, seek_by;
240 struct split_file *sf;
242 sf = (struct split_file *)f->f_fsdata;
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);
259 if (seek_by > 0) {
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.
265 void *tmp;
267 tmp = malloc(SEEK_BUF);
268 if (tmp == NULL) {
269 errno = ENOMEM;
270 return (-1);
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;
282 free(tmp);
283 if (errno != 0)
284 return (-1);
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);
296 sf->tot_pos += new_pos - sf->file_pos;
297 sf->file_pos = new_pos;
300 return (sf->tot_pos);
303 static int
304 splitfs_stat(struct open_file *f, struct stat *sb)
306 int result;
307 struct split_file *sf = (struct split_file *)f->f_fsdata;
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);