]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/libsa/cd9660read.c
kern: cpuset: allow jails to modify child jails' roots
[FreeBSD/FreeBSD.git] / stand / libsa / cd9660read.c
1 /*
2  * Copyright (C) 1996 Wolfgang Solfrank.
3  * Copyright (C) 1996 TooLs GmbH.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *      This product includes software developed by TooLs GmbH.
17  * 4. The name of TooLs GmbH may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 /* Originally derived from libsa/cd9660.c: */
33 /*      $NetBSD: cd9660.c,v 1.5 1997/06/26 19:11:33 drochner Exp $      */
34
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37
38 #include <sys/param.h>
39 #include <fs/cd9660/iso.h>
40 #include <fs/cd9660/cd9660_rrip.h>
41
42 static uint64_t cd9660_lookup(const char *);
43 static ssize_t cd9660_fsread(uint64_t, void *, size_t);
44
45 #define SUSP_CONTINUATION       "CE"
46 #define SUSP_PRESENT            "SP"
47 #define SUSP_STOP               "ST"
48 #define SUSP_EXTREF             "ER"
49 #define RRIP_NAME               "NM"
50
51 typedef struct {
52         ISO_SUSP_HEADER         h;
53         u_char signature        [ISODCL (  5,    6)];
54         u_char len_skp          [ISODCL (  7,    7)]; /* 711 */
55 } ISO_SUSP_PRESENT;
56
57 static int
58 read_iso_block(void *buffer, daddr_t blkno)
59 {
60
61         return (drvread(&dsk, buffer, blkno * 4, 4));
62 }
63
64 static ISO_SUSP_HEADER *
65 susp_lookup_record(const char *identifier, struct iso_directory_record *dp,
66     int lenskip)
67 {
68         static char susp_buffer[ISO_DEFAULT_BLOCK_SIZE];
69         ISO_SUSP_HEADER *sh;
70         ISO_RRIP_CONT *shc;
71         char *p, *end;
72         int error;
73
74         p = dp->name + isonum_711(dp->name_len) + lenskip;
75         /* Names of even length have a padding byte after the name. */
76         if ((isonum_711(dp->name_len) & 1) == 0)
77                 p++;
78         end = (char *)dp + isonum_711(dp->length);
79         while (p + 3 < end) {
80                 sh = (ISO_SUSP_HEADER *)p;
81                 if (bcmp(sh->type, identifier, 2) == 0)
82                         return (sh);
83                 if (bcmp(sh->type, SUSP_STOP, 2) == 0)
84                         return (NULL);
85                 if (bcmp(sh->type, SUSP_CONTINUATION, 2) == 0) {
86                         shc = (ISO_RRIP_CONT *)sh;
87                         error = read_iso_block(susp_buffer,
88                             isonum_733(shc->location));
89
90                         /* Bail if it fails. */
91                         if (error != 0)
92                                 return (NULL);
93                         p = susp_buffer + isonum_733(shc->offset);
94                         end = p + isonum_733(shc->length);
95                 } else {
96                         /* Ignore this record and skip to the next. */
97                         p += isonum_711(sh->length);
98
99                         /* Avoid infinite loops with corrupted file systems */
100                         if (isonum_711(sh->length) == 0)
101                                 return (NULL);
102                 }
103         }
104         return (NULL);
105 }
106
107 static const char *
108 rrip_lookup_name(struct iso_directory_record *dp, int lenskip, size_t *len)
109 {
110         ISO_RRIP_ALTNAME *p;
111
112         if (len == NULL)
113                 return (NULL);
114
115         p = (ISO_RRIP_ALTNAME *)susp_lookup_record(RRIP_NAME, dp, lenskip);
116         if (p == NULL)
117                 return (NULL);
118         switch (*p->flags) {
119         case ISO_SUSP_CFLAG_CURRENT:
120                 *len = 1;
121                 return (".");
122         case ISO_SUSP_CFLAG_PARENT:
123                 *len = 2;
124                 return ("..");
125         case 0:
126                 *len = isonum_711(p->h.length) - 5;
127                 return ((char *)p + 5);
128         default:
129                 /*
130                  * We don't handle hostnames or continued names as they are
131                  * too hard, so just bail and use the default name.
132                  */
133                 return (NULL);
134         }
135 }
136
137 static int
138 rrip_check(struct iso_directory_record *dp, int *lenskip)
139 {
140         ISO_SUSP_PRESENT *sp;
141         ISO_RRIP_EXTREF *er;
142         char *p;
143
144         /* First, see if we can find a SP field. */
145         p = dp->name + isonum_711(dp->name_len);
146         if (p > (char *)dp + isonum_711(dp->length)) {
147                 return (0);
148         }
149         sp = (ISO_SUSP_PRESENT *)p;
150         if (bcmp(sp->h.type, SUSP_PRESENT, 2) != 0) {
151                 return (0);
152         }
153         if (isonum_711(sp->h.length) != sizeof(ISO_SUSP_PRESENT)) {
154                 return (0);
155         }
156         if (sp->signature[0] != 0xbe || sp->signature[1] != 0xef) {
157                 return (0);
158         }
159         *lenskip = isonum_711(sp->len_skp);
160
161         /*
162          * Now look for an ER field.  If RRIP is present, then there must
163          * be at least one of these.  It would be more pedantic to walk
164          * through the list of fields looking for a Rock Ridge ER field.
165          */
166         er = (ISO_RRIP_EXTREF *)susp_lookup_record(SUSP_EXTREF, dp, 0);
167         if (er == NULL) {
168                 return (0);
169         }
170         return (1);
171 }
172
173 static int
174 dirmatch(const char *path, struct iso_directory_record *dp, int use_rrip,
175     int lenskip)
176 {
177         size_t len;
178         const char *cp = NULL;
179         int i, icase;
180
181         if (use_rrip)
182                 cp = rrip_lookup_name(dp, lenskip, &len);
183         else
184                 cp = NULL;
185         if (cp == NULL) {
186                 len = isonum_711(dp->name_len);
187                 cp = dp->name;
188                 icase = 1;
189         } else
190                 icase = 0;
191         for (i = len; --i >= 0; path++, cp++) {
192                 if (!*path || *path == '/')
193                         break;
194                 if (*path == *cp)
195                         continue;
196                 if (!icase && toupper(*path) == *cp)
197                         continue;
198                 return 0;
199         }
200         if (*path && *path != '/') {
201                 return 0;
202         }
203         /*
204          * Allow stripping of trailing dots and the version number.
205          * Note that this will find the first instead of the last version
206          * of a file.
207          */
208         if (i >= 0 && (*cp == ';' || *cp == '.')) {
209                 /* This is to prevent matching of numeric extensions */
210                 if (*cp == '.' && cp[1] != ';') {
211                         return 0;
212                 }
213                 while (--i >= 0)
214                         if (*++cp != ';' && (*cp < '0' || *cp > '9')) {
215                                 return 0;
216                         }
217         }
218         return 1;
219 }
220
221 static uint64_t
222 cd9660_lookup(const char *path)
223 {
224         static char blkbuf[MAX(ISO_DEFAULT_BLOCK_SIZE,
225             sizeof(struct iso_primary_descriptor))];
226         struct iso_primary_descriptor *vd;
227         struct iso_directory_record rec;
228         struct iso_directory_record *dp = NULL;
229         size_t dsize, off;
230         daddr_t bno, boff;
231         int rc, first, use_rrip, lenskip;
232         uint64_t cookie;
233
234         for (bno = 16;; bno++) {
235                 rc = read_iso_block(blkbuf, bno);
236                 vd = (struct iso_primary_descriptor *)blkbuf;
237
238                 if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0)
239                         return (0);
240                 if (isonum_711(vd->type) == ISO_VD_END)
241                         return (0);
242                 if (isonum_711(vd->type) == ISO_VD_PRIMARY)
243                         break;
244         }
245
246         bcopy(vd->root_directory_record, &rec, sizeof(rec));
247         if (*path == '/') path++; /* eat leading '/' */
248
249         first = 1;
250         use_rrip = 0;
251         lenskip = 0;
252         while (*path) {
253                 bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
254                 dsize = isonum_733(rec.size);
255                 off = 0;
256                 boff = 0;
257
258                 while (off < dsize) {
259                         if ((off % ISO_DEFAULT_BLOCK_SIZE) == 0) {
260                                 rc = read_iso_block(blkbuf, bno + boff);
261                                 if (rc) {
262                                         return (0);
263                                 }
264                                 boff++;
265                                 dp = (struct iso_directory_record *) blkbuf;
266                         }
267                         if (isonum_711(dp->length) == 0) {
268                                 /* skip to next block, if any */
269                                 off = boff * ISO_DEFAULT_BLOCK_SIZE;
270                                 continue;
271                         }
272
273                         /* See if RRIP is in use. */
274                         if (first)
275                                 use_rrip = rrip_check(dp, &lenskip);
276
277                         if (dirmatch(path, dp, use_rrip,
278                             first ? 0 : lenskip)) {
279                                 first = 0;
280                                 break;
281                         } else
282                                 first = 0;
283
284                         dp = (struct iso_directory_record *)
285                             ((char *) dp + isonum_711(dp->length));
286                         /* If the new block has zero length, it is padding. */
287                         if (isonum_711(dp->length) == 0) {
288                                 /* Skip to next block, if any. */
289                                 off = boff * ISO_DEFAULT_BLOCK_SIZE;
290                                 continue;
291                         }
292                         off += isonum_711(dp->length);
293                 }
294                 if (off >= dsize) {
295                         return (0);
296                 }
297
298                 rec = *dp;
299                 while (*path && *path != '/') /* look for next component */
300                         path++;
301                 if (*path) path++; /* skip '/' */
302         }
303
304         if ((isonum_711(rec.flags) & 2) != 0) {
305                 return (0);
306         }
307
308         cookie = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
309         cookie = (cookie << 32) | isonum_733(rec.size);
310
311         return (cookie);
312 }
313
314 static ssize_t
315 cd9660_fsread(uint64_t cookie, void *buf, size_t nbytes)
316 {
317         static char blkbuf[ISO_DEFAULT_BLOCK_SIZE];
318         static daddr_t curstart = 0, curblk = 0;
319         daddr_t blk, blk_off;
320         off_t byte_off;
321         size_t size, remaining, n;
322         char *s;
323
324         size = cookie & 0xffffffff;
325         blk = (cookie >> 32) & 0xffffffff;
326
327         /* Make sure we're looking at the right file. */
328         if (((blk << 32) | size) != cookie) {
329                 return (-1);
330         }
331
332         if (blk != curstart) {
333                 curstart = blk;
334                 fs_off = 0;
335         }
336
337         size -= fs_off;
338         if (size < nbytes) {
339                 nbytes = size;
340         }
341         remaining = nbytes;
342         s = buf;
343
344         while (remaining > 0) {
345                 blk_off = fs_off >> ISO_DEFAULT_BLOCK_SHIFT;
346                 byte_off = fs_off & (ISO_DEFAULT_BLOCK_SIZE - 1);
347
348                 if (curblk != curstart + blk_off) {
349                         curblk = curstart + blk_off;
350                         read_iso_block(blkbuf, curblk);
351                 }
352
353                 if (remaining < ISO_DEFAULT_BLOCK_SIZE - byte_off) {
354                         n = remaining;
355                 } else {
356                         n = ISO_DEFAULT_BLOCK_SIZE - byte_off;
357                 }
358                 memcpy(s, blkbuf + byte_off, n);
359                 remaining -= n;
360                 s += n;
361
362                 fs_off += n;
363         }
364
365         return (nbytes);
366 }