Line data Source code
1 : /* $OpenBSD: udf_vnops.c,v 1.67 2018/05/27 06:02:14 visa Exp $ */
2 :
3 : /*
4 : * Copyright (c) 2001, 2002 Scott Long <scottl@freebsd.org>
5 : * All rights reserved.
6 : *
7 : * Redistribution and use in source and binary forms, with or without
8 : * modification, are permitted provided that the following conditions
9 : * are met:
10 : * 1. Redistributions of source code must retain the above copyright
11 : * notice, this list of conditions and the following disclaimer.
12 : * 2. Redistributions in binary form must reproduce the above copyright
13 : * notice, this list of conditions and the following disclaimer in the
14 : * documentation and/or other materials provided with the distribution.
15 : *
16 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 : * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 : * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 : * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 : * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 : * SUCH DAMAGE.
27 : *
28 : * $FreeBSD: src/sys/fs/udf/udf_vnops.c,v 1.50 2005/01/28 14:42:16 phk Exp $
29 : */
30 :
31 : /*
32 : * Ported to OpenBSD by Pedro Martelletto in February 2005.
33 : */
34 :
35 : #include <sys/param.h>
36 : #include <sys/systm.h>
37 : #include <sys/namei.h>
38 : #include <sys/malloc.h>
39 : #include <sys/mutex.h>
40 : #include <sys/stat.h>
41 : #include <sys/buf.h>
42 : #include <sys/pool.h>
43 : #include <sys/lock.h>
44 : #include <sys/mount.h>
45 : #include <sys/vnode.h>
46 : #include <sys/lock.h>
47 : #include <sys/dirent.h>
48 : #include <sys/queue.h>
49 : #include <sys/endian.h>
50 : #include <sys/specdev.h>
51 : #include <sys/unistd.h>
52 :
53 : #include <crypto/siphash.h>
54 :
55 : #include <isofs/udf/ecma167-udf.h>
56 : #include <isofs/udf/udf.h>
57 : #include <isofs/udf/udf_extern.h>
58 :
59 : int udf_bmap_internal(struct unode *, off_t, daddr_t *, uint32_t *);
60 :
61 : struct vops udf_vops = {
62 : .vop_access = udf_access,
63 : .vop_bmap = udf_bmap,
64 : .vop_lookup = udf_lookup,
65 : .vop_getattr = udf_getattr,
66 : .vop_open = udf_open,
67 : .vop_close = udf_close,
68 : .vop_ioctl = udf_ioctl,
69 : .vop_read = udf_read,
70 : .vop_readdir = udf_readdir,
71 : .vop_readlink = udf_readlink,
72 : .vop_inactive = udf_inactive,
73 : .vop_reclaim = udf_reclaim,
74 : .vop_strategy = udf_strategy,
75 : .vop_lock = udf_lock,
76 : .vop_unlock = udf_unlock,
77 : .vop_pathconf = udf_pathconf,
78 : .vop_islocked = udf_islocked,
79 : .vop_print = udf_print
80 : };
81 :
82 : #define UDF_INVALID_BMAP -1
83 :
84 : /* Look up a unode based on the udfino_t passed in and return its vnode */
85 : int
86 0 : udf_hashlookup(struct umount *ump, udfino_t id, int flags, struct vnode **vpp)
87 : {
88 : struct unode *up;
89 : struct udf_hash_lh *lh;
90 : int error;
91 :
92 0 : *vpp = NULL;
93 :
94 : loop:
95 0 : mtx_enter(&ump->um_hashmtx);
96 0 : lh = &ump->um_hashtbl[SipHash24(&ump->um_hashkey, &id, sizeof(id)) &
97 0 : ump->um_hashsz];
98 0 : if (lh == NULL) {
99 0 : mtx_leave(&ump->um_hashmtx);
100 0 : return (ENOENT);
101 : }
102 :
103 0 : LIST_FOREACH(up, lh, u_le) {
104 0 : if (up->u_ino == id) {
105 0 : mtx_leave(&ump->um_hashmtx);
106 0 : error = vget(up->u_vnode, flags);
107 0 : if (error == ENOENT)
108 0 : goto loop;
109 0 : if (error)
110 0 : return (error);
111 0 : *vpp = up->u_vnode;
112 0 : return (0);
113 : }
114 : }
115 :
116 0 : mtx_leave(&ump->um_hashmtx);
117 :
118 0 : return (0);
119 0 : }
120 :
121 : int
122 0 : udf_hashins(struct unode *up)
123 : {
124 : struct umount *ump;
125 : struct udf_hash_lh *lh;
126 :
127 0 : ump = up->u_ump;
128 :
129 0 : vn_lock(up->u_vnode, LK_EXCLUSIVE | LK_RETRY);
130 0 : mtx_enter(&ump->um_hashmtx);
131 0 : lh = &ump->um_hashtbl[SipHash24(&ump->um_hashkey,
132 0 : &up->u_ino, sizeof(up->u_ino)) & ump->um_hashsz];
133 0 : if (lh == NULL)
134 0 : panic("hash entry is NULL, up->u_ino = %d", up->u_ino);
135 0 : LIST_INSERT_HEAD(lh, up, u_le);
136 0 : mtx_leave(&ump->um_hashmtx);
137 :
138 0 : return (0);
139 : }
140 :
141 : int
142 0 : udf_hashrem(struct unode *up)
143 : {
144 : struct umount *ump;
145 : struct udf_hash_lh *lh;
146 :
147 0 : ump = up->u_ump;
148 :
149 0 : mtx_enter(&ump->um_hashmtx);
150 0 : lh = &ump->um_hashtbl[SipHash24(&ump->um_hashkey,
151 0 : &up->u_ino, sizeof(up->u_ino)) & ump->um_hashsz];
152 0 : if (lh == NULL)
153 0 : panic("hash entry is NULL, up->u_ino = %d", up->u_ino);
154 0 : LIST_REMOVE(up, u_le);
155 0 : mtx_leave(&ump->um_hashmtx);
156 :
157 0 : return (0);
158 : }
159 :
160 : int
161 0 : udf_allocv(struct mount *mp, struct vnode **vpp, struct proc *p)
162 : {
163 : int error;
164 0 : struct vnode *vp;
165 :
166 0 : error = getnewvnode(VT_UDF, mp, &udf_vops, &vp);
167 0 : if (error) {
168 0 : printf("udf_allocv: failed to allocate new vnode\n");
169 0 : return (error);
170 : }
171 :
172 0 : *vpp = vp;
173 0 : return (0);
174 0 : }
175 :
176 : /* Convert file entry permission (5 bits per owner/group/user) to a mode_t */
177 : static mode_t
178 0 : udf_permtomode(struct unode *up)
179 : {
180 : uint32_t perm;
181 : uint16_t flags;
182 : mode_t mode;
183 :
184 0 : perm = letoh32(up->u_fentry->perm);
185 0 : flags = letoh16(up->u_fentry->icbtag.flags);
186 :
187 0 : mode = perm & UDF_FENTRY_PERM_USER_MASK;
188 0 : mode |= ((perm & UDF_FENTRY_PERM_GRP_MASK) >> 2);
189 0 : mode |= ((perm & UDF_FENTRY_PERM_OWNER_MASK) >> 4);
190 0 : mode |= ((flags & UDF_ICB_TAG_FLAGS_STICKY) << 4);
191 0 : mode |= ((flags & UDF_ICB_TAG_FLAGS_SETGID) << 6);
192 0 : mode |= ((flags & UDF_ICB_TAG_FLAGS_SETUID) << 8);
193 :
194 0 : return (mode);
195 : }
196 :
197 : int
198 0 : udf_access(void *v)
199 : {
200 0 : struct vop_access_args *ap = v;
201 : struct vnode *vp;
202 : struct unode *up;
203 : mode_t a_mode, mode;
204 :
205 0 : vp = ap->a_vp;
206 0 : up = VTOU(vp);
207 0 : a_mode = ap->a_mode;
208 :
209 0 : if (a_mode & VWRITE) {
210 0 : switch (vp->v_type) {
211 : case VDIR:
212 : case VLNK:
213 : case VREG:
214 0 : return (EROFS);
215 : /* NOTREACHED */
216 : default:
217 : break;
218 : }
219 : }
220 :
221 0 : mode = udf_permtomode(up);
222 :
223 0 : return (vaccess(vp->v_type, mode, up->u_fentry->uid, up->u_fentry->gid,
224 0 : a_mode, ap->a_cred));
225 0 : }
226 :
227 : static int mon_lens[2][12] = {
228 : {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
229 : {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
230 : };
231 :
232 : static int
233 0 : udf_isaleapyear(int year)
234 : {
235 : int i;
236 :
237 0 : i = (year % 4) ? 0 : 1;
238 0 : i &= (year % 100) ? 1 : 0;
239 0 : i |= (year % 400) ? 0 : 1;
240 :
241 0 : return (i);
242 : }
243 :
244 : /*
245 : * This is just a rough hack. Daylight savings isn't calculated and tv_nsec
246 : * is ignored.
247 : * Timezone calculation compliments of Julian Elischer <julian@elischer.org>.
248 : */
249 : static void
250 0 : udf_timetotimespec(struct timestamp *time, struct timespec *t)
251 : {
252 : int i, lpyear, daysinyear, year;
253 : union {
254 : uint16_t u_tz_offset;
255 : int16_t s_tz_offset;
256 : } tz;
257 :
258 : /* DirectCD seems to like using bogus year values */
259 0 : year = letoh16(time->year);
260 0 : if (year < 1970) {
261 0 : t->tv_sec = 0;
262 0 : t->tv_nsec = 0;
263 0 : return;
264 : }
265 :
266 : /* Calculate the time and day */
267 0 : t->tv_nsec = 1000 * time->usec + 100000 * time->hund_usec
268 0 : + 10000000 * time->centisec;
269 0 : t->tv_sec = time->second;
270 0 : t->tv_sec += time->minute * 60;
271 0 : t->tv_sec += time->hour * 3600;
272 0 : t->tv_sec += time->day * 3600 * 24;
273 :
274 : /* Calculate the month */
275 0 : lpyear = udf_isaleapyear(year);
276 0 : for (i = 1; i < time->month; i++)
277 0 : t->tv_sec += mon_lens[lpyear][i] * 3600 * 24;
278 :
279 : /* Speed up the calculation */
280 0 : if (year > 1979)
281 0 : t->tv_sec += 315532800;
282 0 : if (year > 1989)
283 0 : t->tv_sec += 315619200;
284 0 : if (year > 1999)
285 0 : t->tv_sec += 315532800;
286 0 : for (i = 2000; i < year; i++) {
287 0 : daysinyear = udf_isaleapyear(i) + 365 ;
288 0 : t->tv_sec += daysinyear * 3600 * 24;
289 : }
290 :
291 : /*
292 : * Calculate the time zone. The timezone is 12 bit signed 2's
293 : * compliment, so we gotta do some extra magic to handle it right.
294 : */
295 0 : tz.u_tz_offset = letoh16(time->type_tz);
296 0 : tz.u_tz_offset &= 0x0fff;
297 0 : if (tz.u_tz_offset & 0x0800)
298 0 : tz.u_tz_offset |= 0xf000; /* extend the sign to 16 bits */
299 0 : if ((time->type_tz & 0x1000) && (tz.s_tz_offset != -2047))
300 0 : t->tv_sec -= tz.s_tz_offset * 60;
301 :
302 0 : return;
303 0 : }
304 :
305 : int
306 0 : udf_getattr(void *v)
307 : {
308 0 : struct vop_getattr_args *ap = v;
309 : struct vnode *vp;
310 : struct unode *up;
311 : struct vattr *vap;
312 : struct extfile_entry *xfentry;
313 : struct file_entry *fentry;
314 : struct timespec ts;
315 :
316 : ts.tv_sec = 0;
317 :
318 0 : vp = ap->a_vp;
319 0 : vap = ap->a_vap;
320 0 : up = VTOU(vp);
321 :
322 0 : xfentry = up->u_fentry;
323 0 : fentry = (struct file_entry *)up->u_fentry;
324 :
325 0 : vap->va_fsid = up->u_dev;
326 0 : vap->va_fileid = up->u_ino;
327 0 : vap->va_mode = udf_permtomode(up);
328 0 : vap->va_nlink = letoh16(fentry->link_cnt);
329 : /*
330 : * The spec says that -1 is valid for uid/gid and indicates an
331 : * invalid uid/gid. How should this be represented?
332 : */
333 0 : vap->va_uid = (letoh32(fentry->uid) == -1) ? 0 : letoh32(fentry->uid);
334 0 : vap->va_gid = (letoh32(fentry->gid) == -1) ? 0 : letoh32(fentry->gid);
335 0 : vap->va_rdev = 0;
336 0 : if (vp->v_type & VDIR) {
337 0 : vap->va_nlink++; /* Count a reference to ourselves */
338 : /*
339 : * Directories that are recorded within their ICB will show
340 : * as having 0 blocks recorded. Since tradition dictates
341 : * that directories consume at least one logical block,
342 : * make it appear so.
343 : */
344 0 : vap->va_size = up->u_ump->um_bsize;
345 0 : } else
346 0 : vap->va_size = letoh64(fentry->inf_len);
347 0 : if (udf_checktag(&xfentry->tag, TAGID_EXTFENTRY) == 0) {
348 0 : udf_timetotimespec(&xfentry->atime, &vap->va_atime);
349 0 : udf_timetotimespec(&xfentry->mtime, &vap->va_mtime);
350 0 : if ((vp->v_type & VDIR) && xfentry->logblks_rec != 0)
351 0 : vap->va_size =
352 0 : letoh64(xfentry->logblks_rec) * up->u_ump->um_bsize;
353 : } else {
354 0 : udf_timetotimespec(&fentry->atime, &vap->va_atime);
355 0 : udf_timetotimespec(&fentry->mtime, &vap->va_mtime);
356 0 : if ((vp->v_type & VDIR) && fentry->logblks_rec != 0)
357 0 : vap->va_size =
358 0 : letoh64(fentry->logblks_rec) * up->u_ump->um_bsize;
359 : }
360 0 : vap->va_ctime = vap->va_mtime; /* Stored as an Extended Attribute */
361 0 : vap->va_flags = 0;
362 0 : vap->va_gen = 1;
363 0 : vap->va_blocksize = up->u_ump->um_bsize;
364 0 : vap->va_bytes = letoh64(fentry->inf_len);
365 0 : vap->va_type = vp->v_type;
366 0 : vap->va_filerev = 0;
367 :
368 0 : return (0);
369 : }
370 :
371 : int
372 0 : udf_open(void *v)
373 : {
374 0 : return (0); /* Nothing to be done at this point */
375 : }
376 :
377 : int
378 0 : udf_close(void *v)
379 : {
380 0 : return (0); /* Nothing to be done at this point */
381 : }
382 :
383 : /*
384 : * File specific ioctls.
385 : */
386 : int
387 0 : udf_ioctl(void *v)
388 : {
389 0 : return (ENOTTY);
390 : }
391 :
392 : /*
393 : * I'm not sure that this has much value in a read-only filesystem, but
394 : * cd9660 has it too.
395 : */
396 : int
397 0 : udf_pathconf(void *v)
398 : {
399 0 : struct vop_pathconf_args *ap = v;
400 : int error = 0;
401 :
402 0 : switch (ap->a_name) {
403 : case _PC_LINK_MAX:
404 0 : *ap->a_retval = 65535;
405 0 : break;
406 : case _PC_NAME_MAX:
407 0 : *ap->a_retval = NAME_MAX;
408 0 : break;
409 : case _PC_CHOWN_RESTRICTED:
410 0 : *ap->a_retval = 1;
411 0 : break;
412 : case _PC_NO_TRUNC:
413 0 : *ap->a_retval = 1;
414 0 : break;
415 : case _PC_TIMESTAMP_RESOLUTION:
416 0 : *ap->a_retval = 1000; /* 1 microsecond */
417 0 : break;
418 : default:
419 : error = EINVAL;
420 0 : break;
421 : }
422 :
423 0 : return (error);
424 : }
425 :
426 : int
427 0 : udf_read(void *v)
428 : {
429 0 : struct vop_read_args *ap = v;
430 0 : struct vnode *vp = ap->a_vp;
431 0 : struct uio *uio = ap->a_uio;
432 0 : struct unode *up = VTOU(vp);
433 0 : struct buf *bp;
434 0 : uint8_t *data;
435 : off_t fsize, offset;
436 : int error = 0;
437 0 : int size;
438 :
439 0 : if (uio->uio_offset < 0)
440 0 : return (EINVAL);
441 :
442 0 : fsize = letoh64(up->u_fentry->inf_len);
443 :
444 0 : while (uio->uio_offset < fsize && uio->uio_resid > 0) {
445 : offset = uio->uio_offset;
446 0 : size = ulmin(uio->uio_resid, MAXBSIZE);
447 0 : if (size > fsize - offset)
448 0 : size = fsize - offset;
449 0 : error = udf_readatoffset(up, &size, offset, &bp, &data);
450 0 : if (error == 0)
451 0 : error = uiomove(data, (size_t)size, uio);
452 0 : if (bp != NULL) {
453 0 : brelse(bp);
454 0 : bp = NULL;
455 0 : }
456 0 : if (error)
457 : break;
458 : };
459 :
460 0 : return (error);
461 0 : }
462 :
463 : /*
464 : * Translate the name from a CS0 dstring to a 16-bit Unicode String.
465 : * Hooks need to be placed in here to translate from Unicode to the encoding
466 : * that the kernel/user expects. Return the length of the translated string.
467 : */
468 : int
469 0 : udf_transname(char *cs0string, char *destname, int len, struct umount *ump)
470 : {
471 : unicode_t *transname;
472 : int i, unilen = 0, destlen;
473 :
474 0 : if (len > MAXNAMLEN) {
475 : #ifdef DIAGNOSTIC
476 0 : printf("udf_transname(): name too long\n");
477 : #endif
478 0 : return (0);
479 : }
480 :
481 : /* allocate a buffer big enough to hold an 8->16 bit expansion */
482 0 : transname = pool_get(&udf_trans_pool, PR_WAITOK);
483 :
484 0 : if ((unilen = udf_rawnametounicode(len, cs0string, transname)) == -1) {
485 : #ifdef DIAGNOSTIC
486 0 : printf("udf_transname(): Unicode translation failed\n");
487 : #endif
488 0 : pool_put(&udf_trans_pool, transname);
489 0 : return (0);
490 : }
491 :
492 : /* Pack it back to 8-bit Unicode. */
493 0 : for (i = 0; i < unilen ; i++)
494 0 : if (transname[i] & 0xff00)
495 0 : destname[i] = '?'; /* Fudge the 16bit chars */
496 : else
497 0 : destname[i] = transname[i] & 0xff;
498 :
499 0 : pool_put(&udf_trans_pool, transname);
500 :
501 : /* Don't forget to terminate the string. */
502 0 : destname[unilen] = 0;
503 : destlen = unilen;
504 :
505 0 : return (destlen);
506 0 : }
507 :
508 : /*
509 : * Compare a CS0 dstring with a name passed in from the VFS layer. Return
510 : * 0 on a successful match, nonzero otherwise. Unicode work may need to be
511 : * done here also.
512 : */
513 : static int
514 0 : udf_cmpname(char *cs0string, char *cmpname, int cs0len, int cmplen, struct umount *ump)
515 : {
516 : char *transname;
517 : int error = 0;
518 :
519 : /* This is overkill, but not worth creating a new pool */
520 0 : transname = pool_get(&udf_trans_pool, PR_WAITOK);
521 :
522 0 : cs0len = udf_transname(cs0string, transname, cs0len, ump);
523 :
524 : /* Easy check. If they aren't the same length, they aren't equal */
525 0 : if ((cs0len == 0) || (cs0len != cmplen))
526 0 : error = -1;
527 : else
528 0 : error = bcmp(transname, cmpname, cmplen);
529 :
530 0 : pool_put(&udf_trans_pool, transname);
531 :
532 0 : return (error);
533 : }
534 :
535 : struct udf_uiodir {
536 : struct dirent *dirent;
537 : int eofflag;
538 : };
539 :
540 : static int
541 0 : udf_uiodir(struct udf_uiodir *uiodir, struct uio *uio, long off)
542 : {
543 0 : size_t de_size = DIRENT_SIZE(uiodir->dirent);
544 :
545 0 : if (uio->uio_resid < de_size) {
546 0 : uiodir->eofflag = 0;
547 0 : return (-1);
548 : }
549 0 : uiodir->dirent->d_off = off;
550 0 : uiodir->dirent->d_reclen = de_size;
551 :
552 0 : return (uiomove(uiodir->dirent, de_size, uio));
553 0 : }
554 :
555 : static struct udf_dirstream *
556 0 : udf_opendir(struct unode *up, int offset, int fsize, struct umount *ump)
557 : {
558 : struct udf_dirstream *ds;
559 :
560 0 : ds = pool_get(&udf_ds_pool, PR_WAITOK | PR_ZERO);
561 :
562 0 : ds->node = up;
563 0 : ds->offset = offset;
564 0 : ds->ump = ump;
565 0 : ds->fsize = fsize;
566 :
567 0 : return (ds);
568 : }
569 :
570 : static struct fileid_desc *
571 0 : udf_getfid(struct udf_dirstream *ds)
572 : {
573 : struct fileid_desc *fid;
574 : int error, frag_size = 0, total_fid_size;
575 :
576 : /* End of directory? */
577 0 : if (ds->offset + ds->off >= ds->fsize) {
578 0 : ds->error = 0;
579 0 : return (NULL);
580 : }
581 :
582 : /* Grab the first extent of the directory */
583 0 : if (ds->off == 0) {
584 0 : ds->size = 0;
585 0 : error = udf_readatoffset(ds->node, &ds->size, ds->offset,
586 0 : &ds->bp, &ds->data);
587 0 : if (error) {
588 0 : ds->error = error;
589 0 : if (ds->bp != NULL) {
590 0 : brelse(ds->bp);
591 0 : ds->bp = NULL;
592 0 : }
593 0 : return (NULL);
594 : }
595 : }
596 :
597 : /*
598 : * Clean up from a previous fragmented FID.
599 : * Is this the right place for this?
600 : */
601 0 : if (ds->fid_fragment && ds->buf != NULL) {
602 0 : ds->fid_fragment = 0;
603 0 : free(ds->buf, M_UDFFID, 0);
604 0 : }
605 :
606 0 : fid = (struct fileid_desc*)&ds->data[ds->off];
607 :
608 : /*
609 : * Check to see if the fid is fragmented. The first test
610 : * ensures that we don't wander off the end of the buffer
611 : * looking for the l_iu and l_fi fields.
612 : */
613 0 : if (ds->off + UDF_FID_SIZE > ds->size ||
614 0 : ds->off + letoh16(fid->l_iu) + fid->l_fi + UDF_FID_SIZE > ds->size){
615 :
616 : /* Copy what we have of the fid into a buffer */
617 0 : frag_size = ds->size - ds->off;
618 0 : if (frag_size >= ds->ump->um_bsize) {
619 0 : printf("udf: invalid FID fragment\n");
620 0 : ds->error = EINVAL;
621 0 : return (NULL);
622 : }
623 :
624 : /*
625 : * File ID descriptors can only be at most one
626 : * logical sector in size.
627 : */
628 0 : ds->buf = malloc(ds->ump->um_bsize, M_UDFFID, M_WAITOK|M_ZERO);
629 0 : bcopy(fid, ds->buf, frag_size);
630 :
631 : /* Reduce all of the casting magic */
632 0 : fid = (struct fileid_desc*)ds->buf;
633 :
634 0 : if (ds->bp != NULL) {
635 0 : brelse(ds->bp);
636 0 : ds->bp = NULL;
637 0 : }
638 :
639 : /* Fetch the next allocation */
640 0 : ds->offset += ds->size;
641 0 : ds->size = 0;
642 0 : error = udf_readatoffset(ds->node, &ds->size, ds->offset,
643 : &ds->bp, &ds->data);
644 0 : if (error) {
645 0 : ds->error = error;
646 0 : if (ds->bp != NULL) {
647 0 : brelse(ds->bp);
648 0 : ds->bp = NULL;
649 0 : }
650 0 : return (NULL);
651 : }
652 :
653 : /*
654 : * If the fragment was so small that we didn't get
655 : * the l_iu and l_fi fields, copy those in.
656 : */
657 0 : if (frag_size < UDF_FID_SIZE)
658 0 : bcopy(ds->data, &ds->buf[frag_size],
659 0 : UDF_FID_SIZE - frag_size);
660 :
661 : /*
662 : * Now that we have enough of the fid to work with,
663 : * copy in the rest of the fid from the new
664 : * allocation.
665 : */
666 0 : total_fid_size = UDF_FID_SIZE + letoh16(fid->l_iu) + fid->l_fi;
667 0 : if (total_fid_size > ds->ump->um_bsize) {
668 0 : printf("udf: invalid FID\n");
669 0 : ds->error = EIO;
670 0 : return (NULL);
671 : }
672 0 : bcopy(ds->data, &ds->buf[frag_size],
673 0 : total_fid_size - frag_size);
674 :
675 0 : ds->fid_fragment = 1;
676 0 : } else {
677 0 : total_fid_size = letoh16(fid->l_iu) + fid->l_fi + UDF_FID_SIZE;
678 : }
679 :
680 : /*
681 : * Update the offset. Align on a 4 byte boundary because the
682 : * UDF spec says so.
683 : */
684 0 : if (!ds->fid_fragment) {
685 0 : ds->off += (total_fid_size + 3) & ~0x03;
686 0 : } else {
687 0 : ds->off = (total_fid_size - frag_size + 3) & ~0x03;
688 : }
689 0 : ds->this_off = ds->offset + ds->off;
690 :
691 0 : return (fid);
692 0 : }
693 :
694 : static void
695 0 : udf_closedir(struct udf_dirstream *ds)
696 : {
697 :
698 0 : if (ds->bp != NULL) {
699 0 : brelse(ds->bp);
700 0 : ds->bp = NULL;
701 0 : }
702 :
703 0 : if (ds->fid_fragment && ds->buf != NULL)
704 0 : free(ds->buf, M_UDFFID, 0);
705 :
706 0 : pool_put(&udf_ds_pool, ds);
707 0 : }
708 :
709 : #define SELF_OFFSET 1
710 : #define PARENT_OFFSET 2
711 :
712 : int
713 0 : udf_readdir(void *v)
714 : {
715 0 : struct vop_readdir_args *ap = v;
716 : struct vnode *vp;
717 : struct uio *uio;
718 0 : struct dirent dir;
719 : struct unode *up;
720 : struct umount *ump;
721 : struct fileid_desc *fid;
722 0 : struct udf_uiodir uiodir;
723 : struct udf_dirstream *ds;
724 : off_t last_off;
725 : enum { MODE_NORMAL, MODE_SELF, MODE_PARENT } mode;
726 : int error = 0;
727 :
728 0 : vp = ap->a_vp;
729 0 : uio = ap->a_uio;
730 0 : up = VTOU(vp);
731 0 : ump = up->u_ump;
732 0 : uiodir.eofflag = 1;
733 0 : uiodir.dirent = &dir;
734 0 : memset(&dir, 0, sizeof(dir));
735 :
736 : /*
737 : * if asked to start at SELF_OFFSET or PARENT_OFFSET, search
738 : * for the parent ref
739 : */
740 0 : if (uio->uio_offset == SELF_OFFSET) {
741 : mode = MODE_SELF;
742 0 : uio->uio_offset = 0;
743 0 : } else if (uio->uio_offset == PARENT_OFFSET) {
744 : mode = MODE_PARENT;
745 0 : uio->uio_offset = 0;
746 0 : } else
747 : mode = MODE_NORMAL;
748 :
749 : /*
750 : * Iterate through the file id descriptors. Give the parent dir
751 : * entry special attention.
752 : */
753 0 : if (ISSET(up->u_ump->um_flags, UDF_MNT_USES_META)) {
754 0 : up->u_ump->um_start += up->u_ump->um_meta_start;
755 0 : up->u_ump->um_len = up->u_ump->um_meta_len;
756 0 : }
757 0 : ds = udf_opendir(up, uio->uio_offset,
758 0 : letoh64(up->u_fentry->inf_len), up->u_ump);
759 :
760 0 : last_off = ds->offset + ds->off;
761 0 : while ((fid = udf_getfid(ds)) != NULL) {
762 :
763 : /* Should we return an error on a bad fid? */
764 0 : if (udf_checktag(&fid->tag, TAGID_FID)) {
765 0 : printf("Invalid FID tag (%d)\n", fid->tag.id);
766 : error = EIO;
767 0 : break;
768 : }
769 :
770 : /* Is this a deleted file? */
771 0 : if (fid->file_char & UDF_FILE_CHAR_DEL)
772 0 : continue;
773 :
774 0 : if ((fid->l_fi == 0) && (fid->file_char & UDF_FILE_CHAR_PAR)) {
775 : /* Do up the '.' and '..' entries. Dummy values are
776 : * used for the offset since the offset here is
777 : * usually zero, and NFS doesn't like that value
778 : */
779 0 : if (mode == MODE_NORMAL) {
780 0 : dir.d_fileno = up->u_ino;
781 0 : dir.d_type = DT_DIR;
782 0 : dir.d_name[0] = '.';
783 0 : dir.d_name[1] = '\0';
784 0 : dir.d_namlen = 1;
785 0 : error = udf_uiodir(&uiodir, uio, SELF_OFFSET);
786 0 : if (error)
787 : break;
788 : }
789 0 : if (mode != MODE_PARENT) {
790 0 : dir.d_fileno = udf_getid(&fid->icb);
791 0 : dir.d_type = DT_DIR;
792 0 : dir.d_name[0] = '.';
793 0 : dir.d_name[1] = '.';
794 0 : dir.d_name[2] = '\0';
795 0 : dir.d_namlen = 2;
796 0 : error = udf_uiodir(&uiodir, uio, PARENT_OFFSET);
797 0 : }
798 : mode = MODE_NORMAL;
799 0 : } else if (mode != MODE_NORMAL) {
800 0 : continue;
801 : } else {
802 0 : dir.d_namlen = udf_transname(&fid->data[fid->l_iu],
803 0 : &dir.d_name[0], fid->l_fi, ump);
804 0 : dir.d_fileno = udf_getid(&fid->icb);
805 0 : dir.d_type = (fid->file_char & UDF_FILE_CHAR_DIR) ?
806 : DT_DIR : DT_UNKNOWN;
807 0 : error = udf_uiodir(&uiodir, uio, ds->this_off);
808 : }
809 0 : if (error) {
810 : /*
811 : * udf_uiodir() indicates there isn't space for
812 : * another entry by returning -1
813 : */
814 0 : if (error == -1)
815 0 : error = 0;
816 : break;
817 : }
818 0 : last_off = ds->this_off;
819 : }
820 :
821 : /* tell the calling layer whether we need to be called again */
822 0 : *ap->a_eofflag = uiodir.eofflag;
823 0 : uio->uio_offset = last_off;
824 :
825 0 : if (!error)
826 0 : error = ds->error;
827 :
828 0 : udf_closedir(ds);
829 0 : if (ISSET(up->u_ump->um_flags, UDF_MNT_USES_META)) {
830 0 : up->u_ump->um_start = up->u_ump->um_realstart;
831 0 : up->u_ump->um_len = up->u_ump->um_reallen;
832 0 : }
833 :
834 0 : return (error);
835 0 : }
836 :
837 : /* Are there any implementations out there that do soft-links? */
838 : int
839 0 : udf_readlink(void *v)
840 : {
841 0 : return (EOPNOTSUPP);
842 : }
843 :
844 : int
845 0 : udf_strategy(void *v)
846 : {
847 0 : struct vop_strategy_args *ap = v;
848 : struct buf *bp;
849 : struct vnode *vp;
850 : struct unode *up;
851 0 : int maxsize, s, error;
852 :
853 0 : bp = ap->a_bp;
854 0 : vp = bp->b_vp;
855 0 : up = VTOU(vp);
856 :
857 : /* cd9660 has this test reversed, but it seems more logical this way */
858 0 : if (bp->b_blkno != bp->b_lblkno) {
859 : /*
860 : * Files that are embedded in the fentry don't translate well
861 : * to a block number. Reject.
862 : */
863 0 : if (udf_bmap_internal(up, bp->b_lblkno * up->u_ump->um_bsize,
864 : &bp->b_lblkno, &maxsize)) {
865 0 : clrbuf(bp);
866 0 : bp->b_blkno = -1;
867 0 : }
868 : } else {
869 0 : error = VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno, NULL);
870 0 : if (error) {
871 0 : bp->b_error = error;
872 0 : bp->b_flags |= B_ERROR;
873 0 : s = splbio();
874 0 : biodone(bp);
875 0 : splx(s);
876 0 : return (error);
877 : }
878 :
879 0 : if ((long)bp->b_blkno == -1)
880 0 : clrbuf(bp);
881 : }
882 :
883 0 : if ((long)bp->b_blkno == -1) {
884 0 : s = splbio();
885 0 : biodone(bp);
886 0 : splx(s);
887 0 : } else {
888 0 : bp->b_dev = vp->v_rdev;
889 0 : (up->u_devvp->v_op->vop_strategy)(ap);
890 : }
891 :
892 0 : return (0);
893 0 : }
894 :
895 : int
896 0 : udf_lock(void *v)
897 : {
898 0 : struct vop_lock_args *ap = v;
899 0 : struct vnode *vp = ap->a_vp;
900 :
901 0 : return rrw_enter(&VTOU(vp)->u_lock, ap->a_flags & LK_RWFLAGS);
902 : }
903 :
904 : int
905 0 : udf_unlock(void *v)
906 : {
907 0 : struct vop_unlock_args *ap = v;
908 0 : struct vnode *vp = ap->a_vp;
909 :
910 0 : rrw_exit(&VTOU(vp)->u_lock);
911 0 : return 0;
912 : }
913 :
914 : int
915 0 : udf_islocked(void *v)
916 : {
917 0 : struct vop_islocked_args *ap = v;
918 :
919 0 : return rrw_status(&VTOU(ap->a_vp)->u_lock);
920 : }
921 :
922 : int
923 0 : udf_print(void *v)
924 : {
925 0 : struct vop_print_args *ap = v;
926 0 : struct vnode *vp = ap->a_vp;
927 0 : struct unode *up = VTOU(vp);
928 :
929 : /*
930 : * Complete the information given by vprint().
931 : */
932 0 : printf("tag VT_UDF, hash id %u\n", up->u_ino);
933 : #ifdef DIAGNOSTIC
934 0 : printf("\n");
935 : #endif
936 0 : return (0);
937 : }
938 :
939 : int
940 0 : udf_bmap(void *v)
941 : {
942 0 : struct vop_bmap_args *ap = v;
943 : struct unode *up;
944 0 : uint32_t max_size;
945 0 : daddr_t lsector;
946 : int error;
947 :
948 0 : up = VTOU(ap->a_vp);
949 :
950 0 : if (ap->a_vpp != NULL)
951 0 : *ap->a_vpp = up->u_devvp;
952 0 : if (ap->a_bnp == NULL)
953 0 : return (0);
954 :
955 0 : error = udf_bmap_internal(up, ap->a_bn * up->u_ump->um_bsize,
956 : &lsector, &max_size);
957 0 : if (error)
958 0 : return (error);
959 :
960 : /* Translate logical to physical sector number */
961 0 : *ap->a_bnp = lsector << (up->u_ump->um_bshift - DEV_BSHIFT);
962 :
963 : /* Punt on read-ahead for now */
964 0 : if (ap->a_runp)
965 0 : *ap->a_runp = 0;
966 :
967 0 : return (0);
968 0 : }
969 :
970 : /*
971 : * The all powerful VOP_LOOKUP().
972 : */
973 : int
974 0 : udf_lookup(void *v)
975 : {
976 0 : struct vop_lookup_args *ap = v;
977 : struct vnode *dvp;
978 0 : struct vnode *tdp = NULL;
979 0 : struct vnode **vpp = ap->a_vpp;
980 : struct unode *up;
981 : struct umount *ump;
982 : struct fileid_desc *fid = NULL;
983 : struct udf_dirstream *ds;
984 : struct proc *p;
985 : u_long nameiop;
986 : u_long flags;
987 : char *nameptr;
988 : long namelen;
989 : udfino_t id = 0;
990 : int offset, error = 0;
991 : int numdirpasses, fsize;
992 :
993 : extern struct nchstats nchstats;
994 :
995 0 : dvp = ap->a_dvp;
996 0 : up = VTOU(dvp);
997 0 : ump = up->u_ump;
998 0 : nameiop = ap->a_cnp->cn_nameiop;
999 0 : flags = ap->a_cnp->cn_flags;
1000 0 : nameptr = ap->a_cnp->cn_nameptr;
1001 0 : namelen = ap->a_cnp->cn_namelen;
1002 0 : fsize = letoh64(up->u_fentry->inf_len);
1003 0 : p = ap->a_cnp->cn_proc;
1004 0 : *vpp = NULL;
1005 :
1006 : /*
1007 : * Make sure the process can scan the requested directory.
1008 : */
1009 0 : error = VOP_ACCESS(dvp, VEXEC, ap->a_cnp->cn_cred, p);
1010 0 : if (error)
1011 0 : return (error);
1012 :
1013 : /*
1014 : * Check if the (directory, name) tuple has been already cached.
1015 : */
1016 0 : error = cache_lookup(dvp, vpp, ap->a_cnp);
1017 0 : if (error >= 0)
1018 0 : return (error);
1019 : else
1020 : error = 0;
1021 :
1022 : /*
1023 : * If dvp is what's being looked up, then return it.
1024 : */
1025 0 : if (ap->a_cnp->cn_namelen == 1 && ap->a_cnp->cn_nameptr[0] == '.') {
1026 0 : vref(dvp);
1027 0 : *vpp = dvp;
1028 0 : return (0);
1029 : }
1030 :
1031 : /*
1032 : * If this is a LOOKUP and we've already partially searched through
1033 : * the directory, pick up where we left off and flag that the
1034 : * directory may need to be searched twice. For a full description,
1035 : * see /sys/isofs/cd9660/cd9660_lookup.c:cd9660_lookup()
1036 : */
1037 0 : if (nameiop != LOOKUP || up->u_diroff == 0 || up->u_diroff > fsize) {
1038 : offset = 0;
1039 : numdirpasses = 1;
1040 0 : } else {
1041 0 : offset = up->u_diroff;
1042 : numdirpasses = 2;
1043 0 : nchstats.ncs_2passes++;
1044 : }
1045 :
1046 0 : if (ISSET(up->u_ump->um_flags, UDF_MNT_USES_META)) {
1047 0 : up->u_ump->um_start += up->u_ump->um_meta_start;
1048 0 : up->u_ump->um_len = up->u_ump->um_meta_len;
1049 0 : }
1050 : lookloop:
1051 0 : ds = udf_opendir(up, offset, fsize, ump);
1052 :
1053 0 : while ((fid = udf_getfid(ds)) != NULL) {
1054 : /* Check for a valid FID tag. */
1055 0 : if (udf_checktag(&fid->tag, TAGID_FID)) {
1056 0 : printf("udf_lookup: Invalid tag\n");
1057 : error = EIO;
1058 0 : break;
1059 : }
1060 :
1061 : /* Is this a deleted file? */
1062 0 : if (fid->file_char & UDF_FILE_CHAR_DEL)
1063 0 : continue;
1064 :
1065 0 : if ((fid->l_fi == 0) && (fid->file_char & UDF_FILE_CHAR_PAR)) {
1066 0 : if (flags & ISDOTDOT) {
1067 0 : id = udf_getid(&fid->icb);
1068 0 : break;
1069 : }
1070 : } else {
1071 0 : if (!(udf_cmpname(&fid->data[fid->l_iu],
1072 0 : nameptr, fid->l_fi, namelen, ump))) {
1073 0 : id = udf_getid(&fid->icb);
1074 0 : break;
1075 : }
1076 : }
1077 : }
1078 :
1079 0 : if (!error)
1080 0 : error = ds->error;
1081 :
1082 0 : if (error) {
1083 0 : udf_closedir(ds);
1084 0 : if (ISSET(up->u_ump->um_flags, UDF_MNT_USES_META)) {
1085 0 : up->u_ump->um_start = up->u_ump->um_realstart;
1086 0 : up->u_ump->um_len = up->u_ump->um_reallen;
1087 0 : }
1088 0 : return (error);
1089 : }
1090 :
1091 : /* Did we have a match? */
1092 0 : if (id) {
1093 0 : error = udf_vget(ump->um_mountp, id, &tdp);
1094 0 : if (!error) {
1095 : /*
1096 : * Remember where this entry was if it's the final
1097 : * component.
1098 : */
1099 0 : if ((flags & ISLASTCN) && nameiop == LOOKUP)
1100 0 : up->u_diroff = ds->offset + ds->off;
1101 0 : if (numdirpasses == 2)
1102 0 : nchstats.ncs_pass2++;
1103 0 : if (!(flags & LOCKPARENT) || !(flags & ISLASTCN)) {
1104 0 : ap->a_cnp->cn_flags |= PDIRUNLOCK;
1105 0 : VOP_UNLOCK(dvp);
1106 0 : }
1107 :
1108 0 : *vpp = tdp;
1109 0 : }
1110 : } else {
1111 : /* Name wasn't found on this pass. Do another pass? */
1112 0 : if (numdirpasses == 2) {
1113 0 : numdirpasses--;
1114 : offset = 0;
1115 0 : udf_closedir(ds);
1116 0 : goto lookloop;
1117 : }
1118 :
1119 0 : if ((flags & ISLASTCN) &&
1120 0 : (nameiop == CREATE || nameiop == RENAME)) {
1121 : error = EROFS;
1122 0 : } else {
1123 : error = ENOENT;
1124 : }
1125 : }
1126 :
1127 : /*
1128 : * Cache the result of this lookup.
1129 : */
1130 0 : if (flags & MAKEENTRY)
1131 0 : cache_enter(dvp, *vpp, ap->a_cnp);
1132 :
1133 0 : udf_closedir(ds);
1134 0 : if (ISSET(up->u_ump->um_flags, UDF_MNT_USES_META)) {
1135 0 : up->u_ump->um_start = up->u_ump->um_realstart;
1136 0 : up->u_ump->um_len = up->u_ump->um_reallen;
1137 0 : }
1138 :
1139 0 : return (error);
1140 0 : }
1141 :
1142 : int
1143 0 : udf_inactive(void *v)
1144 : {
1145 0 : struct vop_inactive_args *ap = v;
1146 0 : struct vnode *vp = ap->a_vp;
1147 :
1148 : /*
1149 : * No need to sync anything, so just unlock the vnode and return.
1150 : */
1151 0 : VOP_UNLOCK(vp);
1152 :
1153 0 : return (0);
1154 : }
1155 :
1156 : int
1157 0 : udf_reclaim(void *v)
1158 : {
1159 0 : struct vop_reclaim_args *ap = v;
1160 : struct vnode *vp;
1161 : struct unode *up;
1162 :
1163 0 : vp = ap->a_vp;
1164 0 : up = VTOU(vp);
1165 :
1166 0 : if (up != NULL) {
1167 0 : udf_hashrem(up);
1168 0 : if (up->u_devvp) {
1169 0 : vrele(up->u_devvp);
1170 0 : up->u_devvp = 0;
1171 0 : }
1172 :
1173 0 : if (up->u_fentry != NULL)
1174 0 : free(up->u_fentry, M_UDFFENTRY, 0);
1175 :
1176 0 : pool_put(&unode_pool, up);
1177 0 : vp->v_data = NULL;
1178 0 : }
1179 :
1180 0 : return (0);
1181 : }
1182 :
1183 : /*
1184 : * Read the block and then set the data pointer to correspond with the
1185 : * offset passed in. Only read in at most 'size' bytes, and then set 'size'
1186 : * to the number of bytes pointed to. If 'size' is zero, try to read in a
1187 : * whole extent.
1188 : *
1189 : * Note that *bp may be assigned error or not.
1190 : *
1191 : */
1192 : int
1193 0 : udf_readatoffset(struct unode *up, int *size, off_t offset,
1194 : struct buf **bp, uint8_t **data)
1195 : {
1196 : struct umount *ump;
1197 : struct extfile_entry *xfentry = NULL;
1198 : struct file_entry *fentry = NULL;
1199 : struct buf *bp1;
1200 0 : uint32_t max_size;
1201 0 : daddr_t sector;
1202 : int error;
1203 :
1204 0 : ump = up->u_ump;
1205 :
1206 0 : *bp = NULL;
1207 0 : error = udf_bmap_internal(up, offset, §or, &max_size);
1208 0 : if (error == UDF_INVALID_BMAP) {
1209 : /*
1210 : * This error means that the file *data* is stored in the
1211 : * allocation descriptor field of the file entry.
1212 : */
1213 0 : if (udf_checktag(&up->u_fentry->tag, TAGID_EXTFENTRY) == 0) {
1214 : xfentry = up->u_fentry;
1215 0 : *data = &xfentry->data[letoh32(xfentry->l_ea)];
1216 0 : *size = letoh32(xfentry->l_ad);
1217 0 : } else {
1218 0 : fentry = (struct file_entry *)up->u_fentry;
1219 0 : *data = &fentry->data[letoh32(fentry->l_ea)];
1220 0 : *size = letoh32(fentry->l_ad);
1221 : }
1222 0 : return (0);
1223 0 : } else if (error != 0) {
1224 0 : return (error);
1225 : }
1226 :
1227 : /* Adjust the size so that it is within range */
1228 0 : if (*size == 0 || *size > max_size)
1229 0 : *size = max_size;
1230 0 : *size = min(*size, MAXBSIZE);
1231 :
1232 0 : if ((error = udf_readlblks(ump, sector, *size, bp))) {
1233 0 : printf("warning: udf_readlblks returned error %d\n", error);
1234 : /* note: *bp may be non-NULL */
1235 0 : return (error);
1236 : }
1237 :
1238 0 : bp1 = *bp;
1239 0 : *data = (uint8_t *)&bp1->b_data[offset % ump->um_bsize];
1240 0 : return (0);
1241 0 : }
1242 :
1243 : /*
1244 : * Translate a file offset into a logical block and then into a physical
1245 : * block.
1246 : */
1247 : int
1248 0 : udf_bmap_internal(struct unode *up, off_t offset, daddr_t *sector,
1249 : uint32_t *max_size)
1250 : {
1251 : struct umount *ump;
1252 : struct extfile_entry *xfentry;
1253 : struct file_entry *fentry;
1254 : void *icb;
1255 : struct icb_tag *tag;
1256 : uint32_t icblen = 0;
1257 : daddr_t lsector;
1258 : int ad_offset, ad_num = 0;
1259 : int i, p_offset, l_ea, l_ad;
1260 :
1261 0 : ump = up->u_ump;
1262 0 : xfentry = up->u_fentry;
1263 0 : fentry = (struct file_entry *)up->u_fentry;
1264 0 : tag = &fentry->icbtag;
1265 0 : if (udf_checktag(&xfentry->tag, TAGID_EXTFENTRY) == 0) {
1266 0 : l_ea = letoh32(xfentry->l_ea);
1267 0 : l_ad = letoh32(xfentry->l_ad);
1268 0 : } else {
1269 0 : l_ea = letoh32(fentry->l_ea);
1270 0 : l_ad = letoh32(fentry->l_ad);
1271 : }
1272 :
1273 0 : switch (letoh16(tag->strat_type)) {
1274 : case 4:
1275 : break;
1276 :
1277 : case 4096:
1278 0 : printf("Cannot deal with strategy4096 yet!\n");
1279 0 : return (ENODEV);
1280 :
1281 : default:
1282 0 : printf("Unknown strategy type %d\n", tag->strat_type);
1283 0 : return (ENODEV);
1284 : }
1285 :
1286 0 : switch (letoh16(tag->flags) & 0x7) {
1287 : case 0:
1288 : /*
1289 : * The allocation descriptor field is filled with short_ad's.
1290 : * If the offset is beyond the current extent, look for the
1291 : * next extent.
1292 : */
1293 0 : do {
1294 0 : offset -= icblen;
1295 0 : ad_offset = sizeof(struct short_ad) * ad_num;
1296 0 : if (ad_offset > l_ad) {
1297 0 : printf("SFile offset out of bounds (%d > %d)\n",
1298 : ad_offset, l_ad);
1299 0 : return (EINVAL);
1300 : }
1301 :
1302 0 : if (udf_checktag(&up->u_fentry->tag, TAGID_EXTFENTRY) == 0)
1303 0 : icb = GETICB(short_ad, xfentry, l_ea + ad_offset);
1304 : else
1305 0 : icb = GETICB(short_ad, fentry, l_ea + ad_offset);
1306 :
1307 0 : icblen = GETICBLEN(short_ad, icb);
1308 0 : ad_num++;
1309 0 : } while(offset >= icblen);
1310 :
1311 0 : lsector = (offset >> ump->um_bshift) +
1312 0 : letoh32(((struct short_ad *)(icb))->lb_num);
1313 :
1314 0 : *max_size = GETICBLEN(short_ad, icb);
1315 :
1316 0 : break;
1317 : case 1:
1318 : /*
1319 : * The allocation descriptor field is filled with long_ad's
1320 : * If the offset is beyond the current extent, look for the
1321 : * next extent.
1322 : */
1323 0 : do {
1324 0 : offset -= icblen;
1325 0 : ad_offset = sizeof(struct long_ad) * ad_num;
1326 0 : if (ad_offset > l_ad) {
1327 0 : printf("LFile offset out of bounds (%d > %d)\n",
1328 : ad_offset, l_ad);
1329 0 : return (EINVAL);
1330 : }
1331 0 : if (udf_checktag(&up->u_fentry->tag, TAGID_EXTFENTRY) == 0)
1332 0 : icb = GETICB(long_ad, xfentry, l_ea + ad_offset);
1333 : else
1334 0 : icb = GETICB(long_ad, fentry, l_ea + ad_offset);
1335 0 : icblen = GETICBLEN(long_ad, icb);
1336 0 : ad_num++;
1337 0 : } while(offset >= icblen);
1338 :
1339 0 : lsector = (offset >> ump->um_bshift) +
1340 0 : letoh32(((struct long_ad *)(icb))->loc.lb_num);
1341 :
1342 0 : *max_size = GETICBLEN(long_ad, icb);
1343 :
1344 0 : break;
1345 : case 3:
1346 : /*
1347 : * This type means that the file *data* is stored in the
1348 : * allocation descriptor field of the file entry.
1349 : */
1350 0 : *max_size = 0;
1351 0 : *sector = up->u_ino + ump->um_start;
1352 :
1353 0 : return (UDF_INVALID_BMAP);
1354 : case 2:
1355 : /* DirectCD does not use extended_ad's */
1356 : default:
1357 0 : printf("Unsupported allocation descriptor %d\n",
1358 : tag->flags & 0x7);
1359 0 : return (ENODEV);
1360 : }
1361 :
1362 0 : *sector = lsector + ump->um_start;
1363 :
1364 : /*
1365 : * Check the sparing table. Each entry represents the beginning of
1366 : * a packet.
1367 : */
1368 0 : if (ump->um_stbl != NULL) {
1369 0 : for (i = 0; i< ump->um_stbl_len; i++) {
1370 : p_offset =
1371 0 : lsector - letoh32(ump->um_stbl->entries[i].org);
1372 0 : if ((p_offset < ump->um_psecs) && (p_offset >= 0)) {
1373 0 : *sector =
1374 0 : letoh32(ump->um_stbl->entries[i].map) +
1375 : p_offset;
1376 0 : break;
1377 : }
1378 : }
1379 : }
1380 :
1381 0 : return (0);
1382 0 : }
|