diff options
Diffstat (limited to 'isofs/rr.c')
-rw-r--r-- | isofs/rr.c | 600 |
1 files changed, 600 insertions, 0 deletions
diff --git a/isofs/rr.c b/isofs/rr.c new file mode 100644 index 00000000..666eb6ec --- /dev/null +++ b/isofs/rr.c @@ -0,0 +1,600 @@ +/* + Copyright (C) 1997 Free Software Foundation, Inc. + Written by Thomas Bushnell, n/BSG. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +/* Parse Rock-Ridge and related SUSP conformant extensions. */ + +#include <stdio.h> +#include <string.h> +#include "isofs.h" + +/* These tell whether the specified extensions are on or not. */ +int susp_live = 0; +int rock_live = 0; + +/* How far to skip when reading SUSP fields. */ +int susp_skip = 0; + +void +release_rrip (struct rrip_lookup *rr) +{ + if ((rr->valid & VALID_NM) && rr->name) + free (rr->name); + if ((rr->valid & VALID_SL) && rr->target) + free (rr->target); +} + + +/* Work function combining the three interfaces below. */ +static int +rrip_work (struct dirrect *dr, struct rrip_lookup *rr, + char *match_name, char match_name_len, + int initializing, int ignorenm) +{ + void *bp, *terminus; + void *slbuf, *nmbuf; + size_t slbufsize, nmbufsize; + int nomorenm, nomoresl; + + /* Initialize RR */ + rr->valid = 0; + rr->target = rr->name = 0; + + if (!susp_live && !initializing) + return 0; + + /* The only extensions we currently support are rock-ridge, so + this test will cut a lot of useless work. */ + if (!rock_live && !initializing) + return 0; + + /* Initialized the name buffers */ + nmbuf = slbuf = 0; + nmbufsize = slbufsize = 0; + nomorenm = nomoresl = 0; + + /* Find the beginning and end of the SUSP area */ + + /* If this is the root node, from the root directory, then + we look in a special place. This only happens during the two + calls in fetch_root, and we know exactly what the value passed + is there. */ + + if (dr == (struct dirrect *)sblock->root) + { + struct dirrect *p; + off_t filestart; + char *c; + error_t err; + + /* Look at the first directory entry in root. */ + err = calculate_file_start (dr, &filestart, 0); + if (err) + return 0; /* give up */ + p = disk_image + (filestart << store->log2_block_size); + + /* Set C to the system use area. */ + c = p->name + p->namelen; + if ((int)c & 1) + c++; + + /* There needs to be an SUSP SP field right here; make sure there is */ + if (!bcmp (c, "SP\7\1\276\357", 6)) + bp = c; + else if (!bcmp (c + 15, "SP\7\1\276\357", 6)) + /* Detect CD-ROM XA disk */ + bp = c + 15; + else + /* No SUSP, give up. */ + return 0; + + terminus = (char *) p + p->len; + } + else + { + /* It's in the normal place. */ + bp = dr->name + dr->namelen; + if ((int) bp & 1) + bp++; /* must be even */ + bp += susp_skip; /* skip to start of susp area */ + terminus = (char *) dr + dr->len; + } + + + /* Loop across all the fields, processing them one at a time. */ + + while (bp < terminus) + { + struct su_header *susp = bp; + void *body; + + /* Make sure the whole thing fits */ + if (bp + sizeof (struct su_header) > terminus + || bp + susp->len > terminus) + break; + + body = (char *) susp + sizeof (struct su_header); + + /* CE means that further extension fields are elsewhere on + the disk. We just reset the pointers and keep going. */ + if (susp->sig[0] == 'C' + && susp->sig[1] == 'E' + && susp->version == 1) + { + int offset; + int location; + int size; + struct su_ce *ce = body; + + offset = isonum_733 (ce->offset); + location = isonum_733 (ce->continuation); + size = isonum_733 (ce->size); + + /* Reset pointers */ + bp = disk_image + (location * logical_block_size) + offset; + terminus = bp + size; + + /* NOT goto next_field */ + continue; + } + + /* Only on the root node; SP signals that the sharing protocol + is in use. */ + if (initializing + && susp->sig[0] == 'S' + && susp->sig[1] == 'P' + && susp->version == 1) + { + /* Sharing Protocol */ + struct su_sp *sp = body; + + /* Verify magic numbers */ + if (sp->check[0] == SU_SP_CHECK_0 + && sp->check[1] == SU_SP_CHECK_1) + susp_live = 1; + + susp_skip = sp->skip; + + goto next_field; + } + + /* Only on the root node; ER signals that a specified extension + is present. We implement and check for only the Rock Ridge + extension. */ + if (initializing + && susp->sig[0] == 'E' + && susp->sig[1] == 'R' + && susp->version == 1) + { + /* Extension Reference */ + struct su_er *er = body; + char *c; + + /* The only extension we currently support is Rock-Ridge. */ + + /* Make sure the ER field is valid */ + if ((void *) er->more + er->len_id + er->len_des + er->len_src + < terminus) + goto nomatch; + + if (er->ext_ver != ROCK_VERS) + goto nomatch; + + c = er->more; + if (memcmp (ROCK_ID, c, er->len_id)) + goto nomatch; + + /* At this point we know we have Rock-Ridge, but + we check these and moan about it if they are wrong. */ + + c += er->len_id; + if (memcmp (ROCK_DES, c, er->len_des)) + fprintf (stderr, "isofs warning: Rock-Ridge extension description is not standard: %s\n", c); + + c += er->len_des; + if (memcmp (ROCK_SRC, c, er->len_src)) + fprintf (stderr, "isofs warning: Rock-Ridge extensions source is not standard: %s\n", c); + + rock_live = 1; + + nomatch: + goto next_field; + } + + /* PD fields are padding and just get ignored. */ + if (susp->sig[0] == 'P' + && susp->sig[1] == 'D' + && susp->version == 1) + goto next_field; + + /* ST fields mean that there are no more SUSP fields to be processed. */ + if (susp->sig[0] == 'S' + && susp->sig[1] == 'T' + && susp->version == 1) + /* All done */ + break; + + /* The rest are Rock-Ridge, and are not interesting if we are doing + setup. */ + + if (initializing || !rock_live) + goto next_field; + + /* RE is present in a directory entry to mean that the node + is specified by a CL field elsewhere. So this entry needs + to be ignored by anyone who understands CL fields. */ + if (susp->sig[0] == 'R' + && susp->sig[1] == 'E' + && susp->version == 1) + { + rr->valid |= VALID_RE; + + /* No point in parsing anything else now. */ + break; + } + + /* NM identifies the real name of the file; it overrides + the name in the directory. */ + if (susp->sig[0] == 'N' + && susp->sig[1] == 'M' + && susp->version == 1 + && !ignorenm) + { + struct rr_nm *nm = body; + size_t nmlen = susp->len - 5; + char *name; + size_t namelen; + + if (nomorenm) + goto next_field; + + if (nm->flags & NAME_DOT) + { + name = "."; + namelen = 1; + goto finalize_nm; + } + else if (nm->flags & NAME_DOTDOT) + { + name = ".."; + namelen = 2; + goto finalize_nm; + } + else if (nm->flags & NAME_HOST) + { + name = host_name; + namelen = strlen (host_name); + goto finalize_nm; + } + + /* Add this component to the list. */ + + /* We don't store a trailing null here, but we always leave + room for it. The null gets stored in the finalization + code below. */ + if (!nmbuf) + nmbuf = malloc ((nmbufsize = nmlen) + 1); + else + nmbuf = realloc (nmbuf, (nmbufsize += nmlen) + 1); + assert (nmbuf); + + bcopy (nm->name, nmbuf + nmbufsize - nmlen, nmlen); + + if (nm->flags & NAME_CONTINUE) + goto next_field; + + name = nmbuf; + namelen = nmbufsize; + + finalize_nm: + nomorenm = 1; + + /* Is this a failed match? */ + if (match_name && (match_name_len != namelen + || memcmp (match_name, name, match_name_len))) + return 0; + + /* Store the name */ + rr->valid |= VALID_NM; + if (name != nmbuf) + { + rr->name = malloc (namelen + 1); + strcpy (rr->name, name); + } + else + { + rr->name = name; + name[namelen] = '\0'; + } + + if (rr->valid & VALID_CL) + /* Finalize CL processing. */ + goto clrecurse; + + goto next_field; + } + + /* PX gives mode, nlink, uid, and gid posix-style attributes. */ + if (susp->sig[0] == 'P' + && susp->sig[1] == 'X' + && susp->version == 1) + { + struct rr_px *px = body; + + rr->valid |= VALID_PX; + + rr->mode = isonum_733 (px->mode); + rr->nlink = isonum_733 (px->nlink); + rr->uid = isonum_733 (px->uid); + rr->gid = isonum_733 (px->gid); + + goto next_field; + } + + /* PN, for S_ISCHR and S_ISDEV devices gives the magic numbers */ + if (susp->sig[0] == 'P' + && susp->sig[1] == 'N' + && susp->version == 1) + { +#define makedev(maj,min) ((((maj)&0xFF)<<8)+((min)&0xFF)) + struct rr_pn *pn = body; + + rr->valid |= VALID_PN; + rr->rdev = makedev (isonum_733 (pn->high), isonum_733 (pn->low)); + + goto next_field; + } + + /* SL tells, for a symlink, what the target of the link is */ + if (susp->sig[0] == 'S' + && susp->sig[1] == 'L' + && susp->version == 1) + { + struct rr_sl *sl = body; + size_t crlen = susp->len - 5; + struct rr_sl_comp *comp; + void *cp; + size_t targalloced, targused; + + void add_comp (char *cname, size_t cnamelen) + { + if (rr->target == 0) + { + rr->target = malloc (cnamelen * 2); + targused = cnamelen; + targalloced = cnamelen * 2; + } + else if (targused + cnamelen > targalloced) + rr->target = realloc (rr->target, targalloced *= 2); + assert (rr->target); + + bcopy (cname, rr->target + targused, cnamelen); + targused += cnamelen; + } + + if (nomoresl) + goto next_field; + + /* Append the component use fields to the records we are saving + up */ + + if (!slbuf) + slbuf = malloc (slbufsize = crlen); + else + slbuf = realloc (slbuf, slbufsize += crlen); + assert (slbuf); + + bcopy (sl->data, slbuf + slbufsize - crlen, crlen); + + if (sl->flags & 1) + /* We'll finish later. */ + goto next_field; + + /* Do the symlink translation */ + for (cp = slbuf; cp < slbuf + slbufsize; cp += comp->len) + { + comp = (struct rr_sl_comp *)cp; + nomoresl = 1; + + /* Put in a slash after each component as we go, + unless it's a "continuation" component. */ + + if (comp->flags & NAME_DOT) + add_comp ("./", 2); + else if (comp->flags & NAME_DOTDOT) + add_comp ("../", 3); + else if (comp->flags & NAME_ROOT) + { + targused = 0; + add_comp ("/", 1); + } + else if (comp->flags & NAME_VOLROOT) + { + targused = 0; + add_comp (mounted_on, strlen (mounted_on)); + } + else if (comp->flags & NAME_HOST) + { + add_comp (host_name, strlen (host_name)); + add_comp ("/", 1); + } + else + { + add_comp (comp->name, comp->len); + if (!(comp->flags & NAME_CONTINUE)) + add_comp ("/", 1); + } + } + + /* And turn the final character, if it's a slash, into a null. + Otherwise, add a null. */ + if (rr->target[targused - 1] == '/') + rr->target[targused - 1] = '\0'; + else + add_comp ("", 1); + + free (slbuf); + goto next_field; + } + + /* TF gives atime, mtime, ctime (and others we don't care about); + this overrides the time specified in the directory. */ + if (susp->sig[0] == 'T' + && susp->sig[1] == 'F' + && susp->version == 1) + { + char *(*convert)(char *, struct timespec *); + struct rr_tf *tf = body; + char *c; + + if (tf->flags & TF_LONG_FORM) + convert = isodate_84261; + else + convert = isodate_915; + + rr->valid |= VALID_TF; + rr->tfflags = tf->flags; + c = tf->data; + + if (rr->tfflags & TF_CREATION) + c = (*convert) (c, &rr->ctime); + if (rr->tfflags & TF_MODIFY) + c = (*convert) (c, &rr->mtime); + if (rr->tfflags & TF_ACCESS) + c = (*convert) (c, &rr->atime); + + goto next_field; + } + + /* CL means that this entry is a relocated directory. We ignore + the attributes in this directory entry (except for NM); they + are fetched from the "." entry of the directory itself. The + CL field identifies the location of the directory, overriding + the location given in the present directory. This directory + is listed somewhere else too (to keep the format ISO 9660 compliant), + but there's an RE entry on that one so that we ignore it. */ + if (susp->sig[0] == 'C' + && susp->sig[1] == 'L' + && susp->version == 1) + { + struct rr_cl *cl = body; + + rr->realdirent + = disk_image + (isonum_733 (cl->loc) * logical_block_size); + rr->valid |= VALID_CL; + + if (rr->valid & VALID_NM) + { + /* We've gotten all we care about from this node. + Remember the NM name, and load all the contents + from the new location. */ + char *savename; + struct dirrect *realdir; + + clrecurse: + /* It might look like VALID_NM is alway set here, but if + we got here from the exit point of the function, then + VALID_NM is actually clear. */ + + /* Save these, becuase rrip_work will clear them. */ + savename = (rr->valid & VALID_NM) ? rr->name : 0; + realdir = rr->realdirent; + + rrip_work (realdir, rr, 0, 0, 0, 1); + + rr->valid |= VALID_CL; + rr->realdirent = realdir; + if (savename) + { + rr->valid |= VALID_NM; + rr->name = savename; + } + + /* If there's an NM field, then we must have matched + if we got here. */ + return (rr->valid & VALID_NM) ? 1 : 0; + } + + /* We must keep looking for an NM record. When we find one, + the NM code will goto the above piece of code. */ + goto next_field; + } + + /* PL is found in the ".." entry of a relocated directory. + The present directory entry points to the fictitious parent + (the one that holds the fictitious RE link here); the PL + field identifies the real parent (the one that has the CL + entry). */ + if (susp->sig[0] == 'P' + && susp->sig[1] == 'L' + && susp->version == 1) + { + struct rr_pl *pl = body; + + rr->realfilestart = (isonum_733 (pl->loc) + * (logical_block_size + >> store->log2_block_size)); + rr->valid |= VALID_PL; + goto next_field; + } + + next_field: + bp = bp + susp->len; + } + + if (rr->valid & VALID_CL) + goto clrecurse; + + /* If we saw an NM field, then it matched; otherwise we + didn't see one. */ + return rr->valid & VALID_NM ? 1 : 0; +} + +/* Parse extensions for directory entry DR. If we encounter an NM + record, and it does not match NAME (length NAMELEN), then stop + immediately (but do note the NM file in RR->valid) and return zero. + If we encounter no NM record at all, then process all the fields + normally and return zero. If we encounter an NM field which matches + the provided name, then process all the fields and return 1. In any + case, fill RR with information corresponding to the fields we do + encounter. */ +int +rrip_match_lookup (struct dirrect *dr, char *name, size_t namelen, + struct rrip_lookup *rr) +{ + return rrip_work (dr, rr, name, namelen, 0, 0); +} + +/* Parse extensions for dirrect DR and store the results in RR. + If IGNORENM, then do not bother with NM records. */ +void +rrip_lookup (struct dirrect *dr, struct rrip_lookup *rr, int ignorenm) +{ + rrip_work (dr, rr, 0, 0, 0, ignorenm); +} + +/* Scan extensions on dirrect DR looking for the tags that are supposed + to be on the root directory. */ +void +rrip_initialize (struct dirrect *dr) +{ + struct rrip_lookup rr; + rrip_work (dr, &rr, 0, 0, 1, 1); + release_rrip (&rr); +} |