/* $NetBSD$ */ /* * File "udf.c" is part of the UDFclient toolkit. * File $Id: udf.c,v 1.266 2007/12/10 14:46:01 reinoud Exp $ $Name: $ * * Copyright (c) 2003, 2004, 2005, 2006 Reinoud Zandijk * All rights reserved. * * The UDFclient toolkit is distributed under the Clarified Artistic Licence. * A copy of the licence is included in the distribution as * `LICENCE.clearified.artistic' and a copy of the licence can also be * requested at the GNU foundantion's website. * * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/ * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "uscsilib.h" /* for locals */ #include "udf.h" #include "udf_bswap.h" #include "udf_discop.h" #include "udf_unix.h" #include "uio.h" #include /* for scsilib */ const char *dvname="UDF device"; #ifndef MAX # define MAX(a,b) ((a)>(b)?(a):(b)) # define MIN(a,b) ((a)<(b)?(a):(b)) #endif /* #define DEBUG(a) { a; } */ #define DEBUG(a) if (0) { a; } #if 1 extern void udf_dump_descriptor(union dscrptr *dscrpt); extern void udf_dump_vat_table(struct udf_part_mapping *udf_part_mapping); extern void udf_dump_disc_anchors(struct udf_discinfo *disc); extern void udf_dump_alive_sets(void); extern void udf_dump_root_dir(struct udf_mountpoint *mountpoint); extern void udf_dump_timestamp(char *dscr, struct timestamp *t); #else void udf_dump_descriptor(union dscrptr *dscrpt) {}; void udf_dump_vat_table(struct udf_part_mapping *udf_part_mapping) {}; void udf_dump_disc_anchors(struct udf_discinfo *disc) {}; void udf_dump_alive_sets(void) {}; void udf_dump_root_dir(struct udf_mountpoint *mountpoint) {}; void udf_dump_timestamp(char *dscr, struct timestamp *t) {}; #endif /* global settings outside udf.h */ int udf_verbose = UDF_VERBLEV_ACTIONS; #define UDF_INODE_NUM_GUESS 2048 /* predefines */ int udf_validate_tag_and_crc_sums(union dscrptr *dscr); void udf_node_mark_dirty(struct udf_node *udf_node); static void udf_set_imp_id(struct regid *regid); static void udf_set_app_id(struct regid *regid); static void udf_node_unmark_dirty(struct udf_node *udf_node); static void udf_init_desc_tag(struct desc_tag *tag, uint16_t id, uint16_t dscr_ver, uint16_t serial_num); static int udf_translate_icb_filetype_to_dirent_filetype(int udf_filetype); static int udf_remove_directory_prim(struct udf_node *dir_node, struct udf_node *udf_node, char *componentname); static int udf_remove_file_entry(struct udf_node *dir_node, struct udf_node *udf_node, char *componentname); /****************************************************************************************** * * Filename space conversion * ******************************************************************************************/ void udf_to_unix_name(char *result, char *id, int len, struct charspec *chsp) { uint16_t raw_name[1024], unix_name[1024]; uint16_t *inchp, ch; uint8_t *outchp; int ucode_chars, nice_uchars; assert(sizeof(char) == sizeof(uint8_t)); outchp = (uint8_t *) result; if ((chsp->type == 0) && (strcmp((char*) chsp->inf, "OSTA Compressed Unicode") == 0)) { *raw_name = *unix_name = 0; ucode_chars = udf_UncompressUnicode(len, (uint8_t *) id, raw_name); ucode_chars = MIN(ucode_chars, UnicodeLength((unicode_t *) raw_name)); nice_uchars = UDFTransName(unix_name, raw_name, ucode_chars); for (inchp = unix_name; nice_uchars>0; inchp++, nice_uchars--) { ch = *inchp; /* sloppy unicode -> latin */ *outchp++ = ch & 255; if (!ch) break; } *outchp++ = 0; } else { /* assume 8bit char length byte latin-1 */ assert(*id == 8); strncpy((char *) result, (char *) (id+1), strlen((char *) (id+1))); } } void unix_to_udf_name(char *result, char *name, uint8_t *result_len, struct charspec *chsp) { uint16_t raw_name[1024]; int udf_chars, name_len; char *inchp; uint16_t *outchp; /* convert latin-1 or whatever to unicode-16 */ *raw_name = 0; name_len = 0; inchp = name; outchp = raw_name; while (*inchp) { *outchp++ = (uint16_t) (*inchp++); name_len++; } if ((chsp->type == 0) && (strcmp((char *) chsp->inf, "OSTA Compressed Unicode") == 0)) { udf_chars = udf_CompressUnicode(name_len, 8, (unicode_t *) raw_name, (byte *) result); } else { /* assume 8bit char length byte latin-1 */ *result++ = 8; udf_chars = 1; strncpy(result, name + 1, strlen(name+1)); udf_chars += strlen(name); } *result_len = udf_chars; } static char *udf_get_compound_name(struct udf_mountpoint *mountpoint) { static char compound[128+128+32+32+1]; struct charspec *charspec; struct udf_log_vol *udf_log_vol; struct udf_pri_vol *udf_pri_vol; char *unix_name; udf_log_vol = mountpoint->udf_log_vol; udf_pri_vol = udf_log_vol->primary; charspec = &udf_pri_vol->pri_vol->desc_charset; assert(charspec->type == 0); assert(strcmp((const char *) charspec->inf, "OSTA Compressed Unicode")==0); unix_name = compound; udf_to_unix_name(unix_name, udf_pri_vol->pri_vol->volset_id, 128, charspec); strcat(unix_name, ":"); unix_name += strlen(unix_name); udf_to_unix_name(unix_name, udf_pri_vol->pri_vol->vol_id, 32, charspec); strcat(unix_name, ":"); unix_name += strlen(unix_name); udf_to_unix_name(unix_name, udf_log_vol->log_vol->logvol_id, 128, charspec); strcat(unix_name, ":"); unix_name += strlen(unix_name); udf_to_unix_name(unix_name, mountpoint->fileset_desc->fileset_id, 32, charspec); return compound; } /****************************************************************************************** * * Dump helpers for printing out information during parse * ******************************************************************************************/ void udf_dump_long_ad(char *prefix, struct long_ad *adr) { printf("%s at sector %d within partion space %d for %d bytes\n", prefix, udf_rw32(adr->loc.lb_num), udf_rw16(adr->loc.part_num), udf_rw32(adr->len) ); } void udf_dump_id(char *prefix, int len, char *id, struct charspec *chsp) { uint16_t raw_name[1024]; uint16_t *pos, ch; int ucode_chars; if (prefix) printf("%s ", prefix); if ((chsp->type == 0) && (strcmp((char *) chsp->inf, "OSTA Compressed Unicode") == 0)) { /* print the identifier using the OSTA compressed unicode */ printf("`"); ucode_chars = udf_UncompressUnicode(len, (uint8_t *) id, raw_name); for (pos = raw_name; ucode_chars > 0; pos++, ucode_chars--) { ch = *pos; /* OSTA code decompresses to machine endian */ if (!ch) break; if ((ch < 32) || (ch > 255)) { printf("[%d]", ch); } else { printf("%c", ch & 255); } } printf("`"); } else { printf("(roughly) `%s`", id+1); } if (prefix) printf("\n"); } void udf_dump_volume_name(char *prefix, struct udf_log_vol *udf_log_vol) { if (prefix) printf("%s%s", prefix, udf_log_vol->primary->udf_session->session_offset?" (local) ":" "); udf_dump_id(NULL, 128, udf_log_vol->primary->pri_vol->volset_id, &udf_log_vol->primary->pri_vol->desc_charset); printf(":"); udf_dump_id(NULL, 32, udf_log_vol->primary->pri_vol->vol_id, &udf_log_vol->primary->pri_vol->desc_charset); printf(":"); udf_dump_id(NULL, 128, udf_log_vol->log_vol->logvol_id, &udf_log_vol->log_vol->desc_charset); if (prefix) printf("\n"); } /****************************************************************************************** * * UDF tag checkers and size calculator * ******************************************************************************************/ /* not used extensively enough yet */ int udf_check_tag(union dscrptr *dscr) { struct desc_tag *tag = &dscr->tag; uint8_t *pos, sum, cnt; /* check TAG header checksum */ pos = (uint8_t *) tag; sum = 0; for(cnt = 0; cnt < 16; cnt++) { if (cnt != 4) sum += *pos; pos++; } if (sum != tag->cksum) { /* bad tag header checksum; this is not a valid tag */ DEBUG(printf("Bad checksum\n")); return EINVAL; } return 0; } int udf_check_tag_payload(union dscrptr *dscr) { struct desc_tag *tag = &dscr->tag; uint16_t crc; /* check payload CRC if applicable */ if (udf_rw16(tag->desc_crc_len) == 0) return 0; crc = udf_cksum(((uint8_t *) tag) + UDF_DESC_TAG_LENGTH, udf_rw16(tag->desc_crc_len)); if (crc != udf_rw16(tag->desc_crc)) { DEBUG(printf("CRC bad\n")); /* bad payload CRC; this is a broken tag */ return EINVAL; } return 0; } int udf_validate_tag_sum(union dscrptr *dscr) { struct desc_tag *tag = &dscr->tag; uint8_t *pos, sum, cnt; /* calculate TAG header checksum */ pos = (uint8_t *) tag; sum = 0; for(cnt = 0; cnt < 16; cnt++) { if (cnt != 4) sum += *pos; pos++; } tag->cksum = sum; /* 8 bit */ return 0; } /* assumes sector number of descriptor to be allready present */ int udf_validate_tag_and_crc_sums(union dscrptr *dscr) { struct desc_tag *tag = &dscr->tag; uint16_t crc; /* check payload CRC if applicable */ if (udf_rw16(tag->desc_crc_len) > 0) { crc = udf_cksum(((uint8_t *) tag) + UDF_DESC_TAG_LENGTH, udf_rw16(tag->desc_crc_len)); tag->desc_crc = udf_rw16(crc); } /* calculate TAG header checksum */ return udf_validate_tag_sum(dscr); } int udf_check_tag_presence(union dscrptr *dscr, int TAG) { struct desc_tag *tag = &dscr->tag; int error; error = udf_check_tag(dscr); if (error) return error; if (udf_rw16(tag->id) != TAG) { DEBUG(fprintf(stderr, "looking for tag %d but found %d\n", TAG, udf_rw16(tag->id))); return ENOENT; } return 0; } /* * for malloc() purposes ... rather have an upperlimit than an exact size */ int udf_calc_tag_malloc_size(union dscrptr *dscr, uint32_t udf_sector_size) { uint32_t size, tag_id; tag_id = udf_rw16(dscr->tag.id); switch (tag_id) { case TAGID_LOGVOL : size = sizeof(struct logvol_desc) - 1; /* maps[1] */ size += udf_rw32(dscr->lvd.mt_l); break; case TAGID_UNALLOC_SPACE : size = sizeof(struct unalloc_sp_desc) - sizeof(struct extent_ad); /* alloc_desc[1] */ size += udf_rw32(dscr->usd.alloc_desc_num) * sizeof(struct extent_ad); break; case TAGID_FID : size = UDF_FID_SIZE + dscr->fid.l_fi + udf_rw16(dscr->fid.l_iu); size = (size + 3) & ~3; return size; /* RETURN !! */ case TAGID_LOGVOL_INTEGRITY : size = sizeof(struct logvol_int_desc) - sizeof(uint32_t); /* tables[1] */ size += udf_rw32(dscr->lvid.l_iu); size += (2 * udf_rw32(dscr->lvid.num_part) * sizeof(uint32_t)); break; case TAGID_SPACE_BITMAP : size = sizeof(struct space_bitmap_desc) - 1; /* data[1] */ size += udf_rw32(dscr->sbd.num_bytes); break; case TAGID_SPARING_TABLE : size = sizeof(struct udf_sparing_table) - sizeof(struct spare_map_entry); /* entries[1] */ size += udf_rw16(dscr->spt.rt_l) * sizeof(struct spare_map_entry); break; case TAGID_FENTRY : size = sizeof(struct file_entry); size += udf_rw32(dscr->fe.l_ea) + udf_rw32(dscr->fe.l_ad)-1; /* data[0] */ break; case TAGID_EXTFENTRY : size = sizeof(struct extfile_entry); size += udf_rw32(dscr->efe.l_ea) + udf_rw32(dscr->efe.l_ad)-1; /* data[0] */ break; case TAGID_FSD : size = sizeof(struct fileset_desc); break; default : size = sizeof(union dscrptr); break; } if ((size == 0) || (udf_sector_size == 0)) return 0; return ((size + udf_sector_size -1) / udf_sector_size) * udf_sector_size; } /****************************************************************************************** * * Logical to physical adres transformation * ******************************************************************************************/ /* convert (udf_log_vol, vpart_num) to a udf_partion structure */ int udf_logvol_vpart_to_partition(struct udf_log_vol *udf_log_vol, uint32_t vpart_num, struct udf_part_mapping **udf_part_mapping_ptr, struct udf_partition **udf_partition_ptr) { struct udf_volumeset *udf_volumeset; struct udf_partition *udf_partition; struct udf_part_mapping *udf_part_mapping; uint32_t part_num; int found; assert(udf_log_vol); assert(!SLIST_EMPTY(&udf_log_vol->part_mappings)); /* clear result */ if (udf_part_mapping_ptr) *udf_part_mapping_ptr = NULL; if (udf_partition_ptr) *udf_partition_ptr = NULL; /* map the requested partition map to the physical udf partition */ found = 0; SLIST_FOREACH(udf_part_mapping, &udf_log_vol->part_mappings, next_mapping) { if (udf_part_mapping->udf_virt_part_num == vpart_num) { found = 1; break; } } if (!found) { printf("\t\t\tVirtual partition number %d not found!\n", vpart_num); return EINVAL; } assert(udf_part_mapping); /* call me paranoid */ part_num = udf_part_mapping->udf_phys_part_num; /* search for the physical partition information */ udf_volumeset = udf_log_vol->primary->volumeset; SLIST_FOREACH(udf_partition, &udf_volumeset->parts, next_partition) { if (udf_rw16(udf_partition->partition->part_num) == part_num) break; } if (!udf_partition) { printf("\t\t\tNo information known about partition %d yet!\n", part_num); printf("\t\t\t\tPlease insert volume %d of this volumeset and try again\n", udf_part_mapping->vol_seq_num); return ENOENT; } if (udf_part_mapping_ptr) *udf_part_mapping_ptr = udf_part_mapping; if (udf_partition_ptr) *udf_partition_ptr = udf_partition; return 0; } /* no recursive translations yet (UDF OK) */ /* All translation is done in 64 bits to prevent bitrot and returns the PARTITION relative address */ int udf_vpartoff_to_sessionoff(struct udf_log_vol *udf_log_vol, struct udf_part_mapping *udf_part_mapping, struct udf_partition *udf_partition, uint64_t offset, uint64_t *ses_off, uint64_t *trans_valid_len) { struct spare_map_entry *sp_entry; struct udf_node *udf_node; struct udf_allocentry *alloc_entry; uint64_t part_start, part_length; uint64_t eff_sector, eff_offset; uint64_t trans_sector; uint64_t cur_offset; uint32_t len, lb_num, block_offset; uint32_t entry, entries; uint32_t sector_size, lb_size; uint64_t packet_num, packet_rlb; uint64_t packet_len; uint32_t vat_entries, *vat_pos; int flags; assert(udf_part_mapping); assert(udf_partition); assert(ses_off); assert(trans_valid_len); /* not ok, but rather this than a dangling random value */ *ses_off = UINT_MAX; *trans_valid_len = 0; lb_size = udf_log_vol->lb_size; sector_size = udf_log_vol->sector_size; part_start = (uint64_t) udf_rw32(udf_partition->partition->start_loc) * sector_size; part_length = (uint64_t) udf_rw32(udf_partition->partition->part_len) * sector_size; /* get the offset (in bytes) in the partition and check its validity */ if (offset >= part_length) { printf("\t\toffset %"PRIu64" is outside partition %d!\n", offset, udf_rw16(udf_partition->partition->part_num)); return EFAULT; } /* do the address translations based on the partition mapping type */ /* translation of virt/sparable etc. is assumed to be done in logical block sizes */ switch (udf_part_mapping->udf_part_mapping_type) { case UDF_PART_MAPPING_PHYSICAL : /* nothing to be done; physical is logical */ *ses_off = part_start + offset; /* 1:1 */ *trans_valid_len = part_length - offset; /* rest of partition */ return 0; case UDF_PART_MAPPING_VIRTUAL : vat_entries = udf_part_mapping->vat_entries; vat_pos = (uint32_t *) udf_part_mapping->vat_translation; /* this translation is dependent on logical sector numbers */ eff_sector = offset / lb_size; eff_offset = offset % lb_size; /* TODO check range for logical sector against VAT length */ trans_sector = vat_pos[eff_sector]; *ses_off = part_start + (trans_sector * lb_size) + eff_offset; /* trans sectors are in lb->lb ? */ *trans_valid_len = lb_size - eff_offset; /* maximum one logical sector */ return 0; case UDF_PART_MAPPING_SPARABLE : /* this translation is dependent on logical sector numbers */ *ses_off = part_start + offset; /* 1:1 */ eff_sector = offset / lb_size; eff_offset = offset % lb_size; /* transform on packet-length base */ packet_len = udf_rw16(udf_part_mapping->udf_pmap->pms.packet_len); /* in lb */ entries = udf_rw16(udf_part_mapping->sparing_table->rt_l); packet_num = (eff_sector / packet_len) * packet_len; packet_rlb = eff_sector % packet_len; /* within packet */ /* translate this packet; source is in partition, destination is absolute disc address */ sp_entry = &udf_part_mapping->sparing_table->entries[0]; for (entry = 0; entry < entries; entry++) { if (udf_rw32(sp_entry->org) - packet_num == 0) { /* mappings contain absolute disc addresses, so no partition offsets please */ *ses_off = (uint64_t) (udf_rw32(sp_entry->map) + packet_rlb) * lb_size + eff_offset; break; } sp_entry++; } *trans_valid_len = (packet_len - packet_rlb) * lb_size; /* maximum one packet */ return 0; case UDF_PART_MAPPING_META : /* We follow the allocation entries to calculate our offset */ udf_node = udf_part_mapping->meta_file; assert(udf_node->addr_type != UDF_ICB_INTERN_ALLOC); /* find sector in the allocation space */ UDF_MUTEX_LOCK(&udf_node->alloc_mutex); cur_offset = 0; TAILQ_FOREACH(alloc_entry, &udf_node->alloc_entries, next_alloc) { len = alloc_entry->len; lb_num = alloc_entry->lb_num; /* vpart_num = alloc_entry->vpart_num; */ flags = alloc_entry->flags; /* check overlap with this alloc entry */ if (cur_offset + len > offset) { assert(((offset - cur_offset) % lb_size) == 0); /* ought to be on sector boundary */ if (flags != UDF_EXT_ALLOCATED) break; block_offset = offset - cur_offset; *ses_off = part_start + lb_num * lb_size + block_offset; /* 1:1 within the block */ *trans_valid_len = len - block_offset; /* rest of this chunk */ UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); return 0; } cur_offset += len; } /* FOREACH */ UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); printf("\t\toffset %"PRIu64" is not translated within current metadata partition %d file descriptor!\n", offset, udf_rw16(udf_partition->partition->part_num)); return EFAULT; case UDF_PART_MAPPING_ERROR : default : break; } printf("Unsupported or bad mapping %d; can't translate\n", udf_part_mapping->udf_part_mapping_type); return EFAULT; } /****************************************************************************************** * * udf_node creator, destructor and syncer * ******************************************************************************************/ static mode_t udf_perm_to_unix_mode(uint32_t perm) { mode_t mode; mode = ((perm & UDF_FENTRY_PERM_USER_MASK) ); mode |= ((perm & UDF_FENTRY_PERM_GRP_MASK ) >> 2); mode |= ((perm & UDF_FENTRY_PERM_OWNER_MASK) >> 4); return mode; } static uint32_t unix_mode_to_udf_perm(mode_t mode) { uint32_t perm; perm = ((mode & S_IRWXO) ); perm |= ((mode & S_IRWXG) << 2); perm |= ((mode & S_IRWXU) << 4); perm |= ((mode & S_IWOTH) << 3); perm |= ((mode & S_IWGRP) << 5); perm |= ((mode & S_IWUSR) << 7); return perm; } /* * Fill in timestamp structure based on clock_gettime(). Time is reported back as a time_t * accompanied with a nano second field. * * The husec, usec and csec could be relaxed in type. */ static void udf_timespec_to_timestamp(struct timespec *timespec, struct timestamp *timestamp) { struct tm tm; uint64_t husec, usec, csec; bzero(timestamp, sizeof(struct timestamp)); gmtime_r(×pec->tv_sec, &tm); /* * Time type and time zone : see ECMA 1/7.3, UDF 2., 2.1.4.1, 3.1.1. * * Lower 12 bits are two complement signed timezone offset if bit 12 * (method 1) is clear. Otherwise if bit 12 is set, specify timezone * offset to -2047 i.e. unsigned `zero' */ timestamp->type_tz = udf_rw16((1<<12) + 0); /* has to be method 1 for CUT/GMT */ timestamp->year = udf_rw16(tm.tm_year + 1900); timestamp->month = tm.tm_mon + 1; /* `tm' structure uses 0..11 for months */ timestamp->day = tm.tm_mday; timestamp->hour = tm.tm_hour; timestamp->minute = tm.tm_min; timestamp->second = tm.tm_sec; usec = (timespec->tv_nsec + 500) / 1000; /* round (if possible) */ husec = usec / 100; usec -= husec * 100; /* we only want 0-99 in usec */ csec = husec / 100; /* we get 0-99 in csec */ husec -= csec * 100; /* we only want 0-99 in husec */ timestamp->centisec = csec; timestamp->hund_usec = husec; timestamp->usec = usec; } void udf_set_timestamp_now(struct timestamp *timestamp) { struct timespec now; clock_gettime(CLOCK_REALTIME, &now); udf_timespec_to_timestamp(&now, timestamp); } /* implemented as a seperate function to allow tuning */ void udf_set_timespec_now(struct timespec *timespec) { clock_gettime(CLOCK_REALTIME, timespec); } int udf_insanetimespec(struct timespec *check) { struct timespec now; struct tm tm; gmtime_r(&check->tv_sec, &tm); /* since our converters can only deal with timestamps after 1970 we * reject earlier */ if (tm.tm_year < 1970) return 1; /* dont accept values from the future; FFS or NFS might not mind, but * UDF does! */ clock_gettime(CLOCK_REALTIME, &now); if (now.tv_sec < check->tv_sec) return 1; if ((now.tv_sec == check->tv_sec) && (now.tv_nsec < check->tv_nsec)) return 1; return 0; } /* * Timestamp to timespec conversion code is taken with small modifications * from FreeBSD /sys/fs/udf by Scott Long */ static int mon_lens[2][12] = { {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} }; static int udf_isaleapyear(int year) { int i; i = (year % 4) ? 0 : 1; i &= (year % 100) ? 1 : 0; i |= (year % 400) ? 0 : 1; return i; } static void udf_timestamp_to_timespec(struct timestamp *timestamp, struct timespec *timespec) { uint32_t usecs, secs, nsecs; uint16_t tz; int i, lpyear, daysinyear, year; timespec->tv_sec = secs = 0; timespec->tv_nsec = nsecs = 0; /* * DirectCD seems to like using bogus year values. * Distrust time->month especially, since it will be used for an array * index. */ year = udf_rw16(timestamp->year); if ((year < 1970) || (timestamp->month > 12)) { return; } /* Calculate the time and day */ usecs = timestamp->usec + 100*timestamp->hund_usec + 10000*timestamp->centisec; nsecs = usecs * 1000; secs = timestamp->second; secs += timestamp->minute * 60; secs += timestamp->hour * 3600; secs += (timestamp->day-1) * 3600 * 24; /* day : 1-31 */ /* Calclulate the month */ lpyear = udf_isaleapyear(year); for (i = 1; i < timestamp->month; i++) secs += mon_lens[lpyear][i-1] * 3600 * 24; /* month: 1-12 */ for (i = 1970; i < year; i++) { daysinyear = udf_isaleapyear(i) + 365 ; secs += daysinyear * 3600 * 24; } /* * Calculate the time zone. The timezone is 12 bit signed 2's * compliment, so we gotta do some extra magic to handle it right. */ tz = udf_rw16(timestamp->type_tz); tz &= 0x0fff; /* only lower 12 bits are significant */ if (tz & 0x0800) /* sign extention */ tz |= 0xf000; /* TODO check timezone conversion */ #if 1 /* check if we are specified a timezone to convert */ if (udf_rw16(timestamp->type_tz) & 0x1000) if ((int16_t) tz != -2047) secs -= (int16_t) tz * 60; #endif timespec->tv_sec = secs; timespec->tv_nsec = nsecs; } static void udf_node_get_fileinfo(struct udf_node *udf_node, union dscrptr *dscrptr) { struct stat *stat; struct file_entry *file_entry; struct extfile_entry *extfile_entry; struct timestamp *atime, *mtime, *ctime, *attrtime; uint64_t inf_len, unique_id; uint32_t uid, gid, udf_perm; uint16_t fe_tag; uint16_t udf_icbtag_flags, serial_num, link_cnt; uint8_t filetype; assert(udf_node); assert(dscrptr); stat = &udf_node->stat; /* check if its an normal file entry or a extended file entry ICB */ fe_tag = udf_rw16(dscrptr->tag.id); if (fe_tag == TAGID_FENTRY) { file_entry = &dscrptr->fe; #if 0 prev_direct_entries = udf_rw32(file_entry->icbtag.prev_num_dirs); strat_param16 = udf_rw16(* (uint16_t *) (file_entry->icbtag.strat_param)); entries = udf_rw16(file_entry->icbtag.max_num_entries); strategy = udf_rw16(file_entry->icbtag.strat_type); data_length = udf_rw32(file_entry->l_ad); addr_type = udf_rw16(file_entry->icbtag.flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK; pos = &file_entry->data[0] + udf_rw32(file_entry->l_ea); #endif filetype = file_entry->icbtag.file_type; inf_len = udf_rw64(file_entry->inf_len); uid = udf_rw32(file_entry->uid); gid = udf_rw32(file_entry->gid); udf_perm = udf_rw32(file_entry->perm); serial_num = udf_rw16(file_entry->tag.serial_num); udf_icbtag_flags = udf_rw16(file_entry->icbtag.flags); link_cnt = udf_rw16(file_entry->link_cnt); unique_id = udf_rw64(file_entry->unique_id); atime = &file_entry->atime; mtime = &file_entry->mtime; ctime = &file_entry->mtime; /* XXX assumption */ attrtime = &file_entry->attrtime; } else if (fe_tag == TAGID_EXTFENTRY) { extfile_entry = &dscrptr->efe; #if 0 prev_direct_entries = udf_rw32(extfile_entry->icbtag.prev_num_dirs); strat_param16 = udf_rw16(* (uint16_t *) (extfile_entry->icbtag.strat_param)); entries = udf_rw16(extfile_entry->icbtag.max_num_entries); strategy = udf_rw16(extfile_entry->icbtag.strat_type); data_length = udf_rw32(extfile_entry->l_ad); addr_type = udf_rw16(extfile_entry->icbtag.flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK; pos = &extfile_entry->data[0] + udf_rw32(extfile_entry->l_ea); #endif filetype = extfile_entry->icbtag.file_type; inf_len = udf_rw64(extfile_entry->inf_len); uid = udf_rw32(extfile_entry->uid); gid = udf_rw32(extfile_entry->gid); udf_perm = udf_rw32(extfile_entry->perm); serial_num = udf_rw16(extfile_entry->tag.serial_num); udf_icbtag_flags = udf_rw16(extfile_entry->icbtag.flags); link_cnt = udf_rw16(extfile_entry->link_cnt); /* how many FID's are linked to this (ext)fentry */ unique_id = udf_rw64(extfile_entry->unique_id); /* unique file ID */ atime = &extfile_entry->atime; mtime = &extfile_entry->mtime; ctime = &extfile_entry->ctime; attrtime = &extfile_entry->attrtime; } else { printf("udf_node_set_file_info : help! i can't be here!!! i got a %d tag\n", fe_tag); udf_dump_descriptor(dscrptr); return; } /* fill in (parts of) the stat structure */ /* XXX important info missing still like access mode, times etc. XXX */ udf_node->udf_filetype = filetype; udf_node->serial_num = serial_num; udf_node->udf_icbtag_flags = udf_icbtag_flags; udf_node->link_cnt = link_cnt; /* how many FID's are linked to this (ext)fentry */ udf_node->unique_id = unique_id; /* unique file ID */ /* fill in stat basics */ bzero(stat, sizeof(struct stat)); stat->st_ino = unique_id; /* lowest 32 bit(!) only */ stat->st_mode = udf_perm_to_unix_mode(udf_perm); /* CONVERT from udf_perm */ stat->st_mode |= (udf_translate_icb_filetype_to_dirent_filetype(filetype) & DT_DIR) ? S_IFDIR : S_IFREG; stat->st_uid = uid; stat->st_gid = gid; /* ... times */ udf_timestamp_to_timespec(atime, &stat->st_atimespec); udf_timestamp_to_timespec(mtime, &stat->st_mtimespec); udf_timestamp_to_timespec(attrtime, &stat->st_ctimespec); #ifndef NO_STAT_BIRTHTIME udf_timestamp_to_timespec(ctime, &stat->st_birthtimespec); #endif /* ... sizes */ stat->st_size = inf_len; stat->st_blksize = udf_node->udf_log_vol->lb_size; /* special: updatables */ stat->st_nlink = link_cnt; stat->st_blocks = (stat->st_size + 512 -1)/512; /* blocks are hardcoded 512 bytes/sector in stat :-/ */ return; } static void udf_node_set_fileinfo(struct udf_node *udf_node, union dscrptr *dscrptr) { struct stat *stat; struct file_entry *file_entry; struct extfile_entry *extfile_entry; struct timestamp *atime, *mtime, *ctime, *attrtime; uint64_t inf_len, unique_id; uint32_t uid, gid, udf_perm; uint16_t fe_tag, serial_num, link_cnt; uint8_t filetype; assert(udf_node); assert(dscrptr); stat = &udf_node->stat; /* set (parts of) the stat structure */ /* XXX important info missing still like times etc. XXX */ uid = stat->st_uid; gid = stat->st_gid; inf_len = stat->st_size; udf_perm = unix_mode_to_udf_perm(stat->st_mode); /* conversion to UDF perm. */ filetype = udf_node->udf_filetype; unique_id = udf_node->unique_id; serial_num = udf_node->serial_num; link_cnt = udf_node->link_cnt; /* check if its to be written in an normal file entry or a extended file entry ICB */ fe_tag = udf_rw16(dscrptr->tag.id); if (fe_tag == TAGID_FENTRY) { file_entry = &dscrptr->fe; #if 0 prev_direct_entries = udf_rw32(file_entry->icbtag.prev_num_dirs); strat_param16 = udf_rw16(* (uint16_t *) (file_entry->icbtag.strat_param)); entries = udf_rw16(file_entry->icbtag.max_num_entries); strategy = udf_rw16(file_entry->icbtag.strat_type); data_length = udf_rw32(file_entry->l_ad); addr_type = udf_rw16(file_entry->icbtag.flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK; pos = &file_entry->data[0] + udf_rw32(file_entry->l_ea); #endif file_entry->icbtag.file_type = filetype; file_entry->inf_len = udf_rw64(inf_len); file_entry->uid = udf_rw32(uid); file_entry->gid = udf_rw32(gid); file_entry->perm = udf_rw32(udf_perm); file_entry->tag.serial_num = udf_rw16(serial_num); file_entry->link_cnt = udf_rw16(link_cnt); file_entry->unique_id = udf_rw64(unique_id); atime = &file_entry->atime; mtime = &file_entry->mtime; ctime = mtime; /* XXX assumption */ attrtime = &file_entry->attrtime; } else if (fe_tag == TAGID_EXTFENTRY) { extfile_entry = &dscrptr->efe; #if 0 prev_direct_entries = udf_rw32(extfile_entry->icbtag.prev_num_dirs); strat_param16 = udf_rw16(* (uint16_t *) (extfile_entry->icbtag.strat_param)); entries = udf_rw16(extfile_entry->icbtag.max_num_entries); strategy = udf_rw16(extfile_entry->icbtag.strat_type); data_length = udf_rw32(extfile_entry->l_ad); addr_type = udf_rw16(extfile_entry->icbtag.flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK; pos = &extfile_entry->data[0] + udf_rw32(extfile_entry->l_ea); #endif extfile_entry->icbtag.file_type = filetype; extfile_entry->inf_len = udf_rw64(inf_len); extfile_entry->uid = udf_rw32(uid); extfile_entry->gid = udf_rw32(gid); extfile_entry->perm = udf_rw32(udf_perm); extfile_entry->tag.serial_num = udf_rw16(serial_num); extfile_entry->link_cnt = udf_rw16(link_cnt); /* how many FID's are linked to this (ext)fentry */ extfile_entry->unique_id = udf_rw64(unique_id); /* unique file ID */ atime = &extfile_entry->atime; mtime = &extfile_entry->mtime; ctime = &extfile_entry->ctime; attrtime = &extfile_entry->attrtime; } else { printf("udf_node_set_file_info : help! i can't be here!!! i got a %d tag\n", fe_tag); udf_dump_descriptor(dscrptr); return; } /* FILL in {atime, mtime, attrtime} TIMES! */ #ifndef NO_STAT_BIRTHTIME udf_timespec_to_timestamp(&stat->st_birthtimespec, ctime); #endif udf_timespec_to_timestamp(&stat->st_atimespec, atime); udf_timespec_to_timestamp(&stat->st_mtimespec, mtime); udf_timespec_to_timestamp(&stat->st_ctimespec, attrtime); return; } /* with 32 bits ino_t, a maximum of about 8 TB discs are supported (1<<32) * 2KB*/ __inline ino_t udf_calc_hash(struct long_ad *icbptr) { /* TODO unique file-id would be better */ return (ino_t) udf_rw32(icbptr->loc.lb_num); } void udf_insert_node_in_hash(struct udf_node *udf_node) { struct udf_log_vol *log_vol; uint32_t bucket; ino_t hashkey; struct long_ad icb; icb.loc.lb_num = udf_rw32(TAILQ_FIRST(&udf_node->dscr_allocs)->lb_num); log_vol = udf_node->udf_log_vol; hashkey = udf_calc_hash(&icb); udf_node->hashkey = hashkey; bucket = hashkey & UDF_INODE_HASHMASK; LIST_INSERT_HEAD(&log_vol->udf_nodes[bucket], udf_node, next_node); } /* dispose udf_node's administration */ void udf_dispose_udf_node(struct udf_node *udf_node) { struct udf_allocentry *alloc_entry; struct udf_buf *buf_entry; struct udf_node *lookup; uint32_t bucket; ino_t hashkey; if (!udf_node) return; /* XXX locks? XXX */ UDF_MUTEX_LOCK(&udf_node->alloc_mutex); if (udf_node->dirty) { DEBUG(printf("Warning: disposing dirty node\n")); udf_node_unmark_dirty(udf_node); } /* free all associated buffers */ UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock); UDF_MUTEX_LOCK(&udf_node->buf_mutex); /* due to this `trick' we dont need to use a marker */ while ((buf_entry = TAILQ_FIRST(&udf_node->vn_bufs))) { udf_mark_buf_clean(udf_node, buf_entry); /* its destroyed so not dirty */ udf_mark_buf_allocated(udf_node, buf_entry); /* i.e. taken care of */ udf_detach_buf_from_node(udf_node, buf_entry); udf_free_buf_entry(buf_entry); } /* free in-node filedata blob if present */ if (udf_node->intern_data) free(udf_node->intern_data); UDF_MUTEX_UNLOCK(&udf_node->buf_mutex); UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); /* free our extended attribute administration if present */ if (udf_node->extattrfile_icb) free(udf_node->extattrfile_icb); if (udf_node->streamdir_icb) free(udf_node->streamdir_icb); if (udf_node->extended_attr) free(udf_node->extended_attr); /* free dscr_allocs queue */ while ((alloc_entry = TAILQ_FIRST(&udf_node->dscr_allocs))) { TAILQ_REMOVE(&udf_node->dscr_allocs, alloc_entry, next_alloc); free(alloc_entry); } /* free allocation queue */ while ((alloc_entry = TAILQ_FIRST(&udf_node->alloc_entries))) { TAILQ_REMOVE(&udf_node->alloc_entries, alloc_entry, next_alloc); free(alloc_entry); } /* if its part of a logical volume, delete it in its hash table */ if (udf_node->udf_log_vol) { hashkey = udf_node->hashkey; bucket = hashkey & UDF_INODE_HASHMASK; LIST_FOREACH(lookup, &udf_node->udf_log_vol->udf_nodes[bucket], next_node) { /* hashkey doesn't matter; just remove same udf_node pointer */ if (lookup == udf_node) { assert(lookup->hashkey == hashkey); DEBUG(printf("removal of udf_node from the hash table\n")); LIST_REMOVE(lookup, next_node); break; } } } UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); /* free the node */ free(udf_node); } uint64_t udf_increment_unique_id(struct udf_log_vol *udf_log_vol) { uint64_t unique_id, next_unique_id; /* lock? */ unique_id = udf_log_vol->next_unique_id; /* increment according to UDF 3/3.2.1.1 */ next_unique_id = unique_id + 1; if (((next_unique_id << 32) >> 32) < 16) next_unique_id |= 16; udf_log_vol->next_unique_id = next_unique_id; DEBUG(printf("next unique_id <-- %"PRIu64"\n", udf_log_vol->next_unique_id)); return unique_id; } int udf_init_udf_node(struct udf_mountpoint *mountpoint, struct udf_log_vol *udf_log_vol, char *what, struct udf_node **udf_nodeptr) { struct udf_node *udf_node; uint32_t lb_size, data_space_avail; int descr_ver; assert(udf_log_vol); lb_size = udf_log_vol->lb_size; /* get ourselves some space */ udf_node = calloc(1, sizeof(struct udf_node)); if (!udf_node) return ENOMEM; /* setup basic udf_node */ udf_node->addr_type = UDF_ICB_LONG_ALLOC; udf_node->icb_len = sizeof(struct long_ad); udf_node->serial_num = 1; udf_node->udf_icbtag_flags = UDF_ICB_INTERN_ALLOC; udf_node->link_cnt = 1; /* how many FID's are linked to this node */ udf_node->unique_id = 0; /* unique file ID unknown */ /* get internal space available for internal nodes */ /* TODO keep in mind the extended attributes! XXX */ descr_ver = udf_rw16(udf_log_vol->log_vol->tag.descriptor_ver); if (descr_ver == 2) { /* TODO reserve some space for the icb creation time */ data_space_avail = udf_log_vol->lb_size - sizeof(struct file_entry) - 0; /* udf_rw32(file_entry->l_ea) */ } else { data_space_avail = udf_log_vol->lb_size - sizeof(struct extfile_entry) - 0; /* udf_rw32(extfile_entry->l_ea) */ } udf_node->intern_free = data_space_avail; udf_node->intern_data = NULL; udf_node->intern_len = 0; /* finalise udf_node */ udf_node->mountpoint = mountpoint; udf_node->udf_log_vol = udf_log_vol; TAILQ_INIT(&udf_node->dscr_allocs); TAILQ_INIT(&udf_node->alloc_entries); TAILQ_INIT(&udf_node->vn_bufs); UDF_MUTEX_INIT(&udf_node->alloc_mutex); UDF_MUTEX_INIT(&udf_node->buf_mutex); /* XXX udf_node_lock NOT USED XXX */ /* pthread_rwlock_init(&udf_node->udf_node_lock, NULL); */ *udf_nodeptr = udf_node; return 0; } int udf_allocate_udf_node_on_disc(struct udf_node *udf_node) { struct udf_allocentry *alloc_entry; uint32_t lb_num, lb_size; uint16_t vpart_num; int error; assert(udf_node); assert(udf_node->udf_log_vol); assert(udf_node->udf_log_vol->log_vol); lb_size = udf_node->udf_log_vol->lb_size; assert(lb_size); /* pre-allocate node; its needed in directory linkage for now */ error = udf_allocate_lbs(udf_node->udf_log_vol, UDF_C_NODE, /*num lb */ 1, "New FID", &vpart_num, &lb_num, NULL); if (error) return error; alloc_entry = calloc(1, sizeof(struct udf_allocentry)); if (!alloc_entry) { return ENOMEM; } alloc_entry->len = lb_size; alloc_entry->vpart_num = vpart_num; alloc_entry->lb_num = lb_num; alloc_entry->flags = 0; TAILQ_INSERT_TAIL(&udf_node->dscr_allocs, alloc_entry, next_alloc); assert(error == 0); return error; } /* note: the udf_node (inode) is not stored in a hashtable! hash value is still invalid */ /* TODO remember our extended attributes and remember our streamdir long_ad */ int udf_readin_anon_udf_node(struct udf_log_vol *udf_log_vol, union dscrptr *given_dscrptr, struct long_ad *udf_icbptr, char *what, struct udf_node **udf_nodeptr) { union dscrptr *dscrptr; struct udf_node *udf_node; struct udf_allocentry *alloc_entry; struct udf_allocentry *cur_alloc, *next_alloc; struct file_entry *file_entry; struct extfile_entry *extfile_entry; struct alloc_ext_entry *alloc_ext_entry; struct long_ad *l_ad; struct short_ad *s_ad; uint64_t inf_len, calculated_len; uint32_t lb_size, entries; uint64_t data_length; uint32_t data_space_avail; uint32_t fe_tag; uint64_t len; uint32_t lb_num, vpart_num; uint32_t icb_len; int16_t addr_type; uint8_t *file_data_pos; uint8_t *pos; uint8_t flags; int error, advance_sector; if ((udf_icbptr->loc.lb_num == 0) && (udf_icbptr->loc.part_num == 0) && (udf_icbptr->len == 0)) return ENOENT; DEBUG(printf("udf_readin_anon_udf_node for %s\n", what)); error = udf_init_udf_node(/*mountpoint*/ NULL, udf_log_vol, what, &udf_node); DEBUG( if (error) printf("While reading in `anononymous' udf_node : got error %s\n", strerror(error)); ); if (error) return error; *udf_nodeptr = udf_node; assert(udf_log_vol); lb_size = udf_log_vol->lb_size; /* read in descriptor if not provided to us */ dscrptr = NULL; len = lb_size; /* nodes are defined in lb_size only (?) */ lb_num = udf_rw32(udf_icbptr->loc.lb_num); vpart_num = udf_rw16(udf_icbptr->loc.part_num); if (!given_dscrptr) { error = udf_read_logvol_descriptor(udf_log_vol, vpart_num, lb_num, what, &dscrptr, NULL); if (error) { *udf_nodeptr = NULL; return error; } } else { dscrptr = given_dscrptr; } fe_tag = udf_rw16(dscrptr->tag.id); if (fe_tag != TAGID_FENTRY && fe_tag != TAGID_EXTFENTRY) { /* something wrong with this address; it doesn't start with a file entry */ printf("UDF: bad udf_node for %s; got a %d tag\n", what, fe_tag); if (dscrptr != given_dscrptr) free(dscrptr); udf_dispose_udf_node(udf_node); *udf_nodeptr = NULL; return EFAULT; } /* get as much info as possible */ udf_node_get_fileinfo(udf_node, dscrptr); /* reset pending write count */ udf_node->v_numoutput = 0; /* initialise various variables for extracting allocation information */ inf_len = 0; data_length = 0; lb_num = 0; len = 0; vpart_num = 0; pos = NULL; data_space_avail = 0; next_alloc = calloc(1, sizeof(struct udf_allocentry)); if (!next_alloc) { if (dscrptr != given_dscrptr) free(dscrptr); udf_dispose_udf_node(udf_node); *udf_nodeptr = NULL; return ENOMEM; } next_alloc->len = lb_size; next_alloc->lb_num = udf_rw32(udf_icbptr->loc.lb_num); next_alloc->vpart_num = udf_rw16(udf_icbptr->loc.part_num); entries = 1; file_data_pos = NULL; calculated_len = 0; error = 0; addr_type = -1; do { /* keep a trace of the descriptor locations in this udf_node */ UDF_VERBOSE_MAX(udf_dump_descriptor(dscrptr)); cur_alloc = next_alloc; next_alloc = calloc(1, sizeof(struct udf_allocentry)); if (!next_alloc) { if (dscrptr != given_dscrptr) free(dscrptr); udf_dispose_udf_node(udf_node); *udf_nodeptr = NULL; return ENOMEM; } memcpy(next_alloc, cur_alloc, sizeof(struct udf_allocentry)); TAILQ_INSERT_TAIL(&udf_node->dscr_allocs, cur_alloc, next_alloc); /* process this allocation descriptor */ /* note that we don't store the file descriptors -> XXX impl. use stuff gets lost here */ fe_tag = udf_rw16(dscrptr->tag.id); switch (fe_tag) { case TAGID_FENTRY : /* allocation descriptors follow this tag */ file_entry = &dscrptr->fe; entries = udf_rw16(file_entry->icbtag.max_num_entries); data_length = udf_rw32(file_entry->l_ad); pos = &file_entry->data[0] + udf_rw32(file_entry->l_ea); inf_len = udf_rw64(file_entry->inf_len); addr_type = udf_rw16(file_entry->icbtag.flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK; data_space_avail = lb_size - sizeof(struct file_entry) - udf_rw32(file_entry->l_ea); /* process extended attributes */ break; case TAGID_EXTFENTRY : /* allocation descriptors follow this tag */ extfile_entry = &dscrptr->efe; entries = udf_rw16(extfile_entry->icbtag.max_num_entries); data_length = udf_rw32(extfile_entry->l_ad); pos = &extfile_entry->data[0] + udf_rw32(extfile_entry->l_ea); inf_len = udf_rw64(extfile_entry->inf_len); addr_type = udf_rw16(extfile_entry->icbtag.flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK; data_space_avail = lb_size - sizeof(struct extfile_entry) - udf_rw32(extfile_entry->l_ea); /* process extended attributes */ break; case TAGID_ALLOCEXTENT : /* allocation descriptors follow this tag; treat as if continuation of a (ext)file entry*/ alloc_ext_entry = &dscrptr->aee; data_length = udf_rw32(alloc_ext_entry->l_ad); pos = &alloc_ext_entry->data[0]; assert(addr_type >= 0); break; case TAGID_INDIRECT_ENTRY : printf("create_anon_udf_node called with indirect entry; following chain\n"); l_ad = &dscrptr->inde.indirect_icb; next_alloc->len = udf_rw32(l_ad->len); next_alloc->lb_num = udf_rw32(l_ad->loc.lb_num); next_alloc->vpart_num = udf_rw16(l_ad->loc.part_num); entries = 1; /* at least one more entry */ advance_sector = 0; /* don't advance to next sector */ break; default: printf("read_file_part_extents: i can't be here! (tag = %d) in ICB hiargy of %s\n", fe_tag, what); udf_dump_descriptor(dscrptr); return EFAULT; } if (fe_tag != TAGID_INDIRECT_ENTRY) { udf_node->addr_type = addr_type; if (addr_type == UDF_ICB_INTERN_ALLOC) { udf_node->intern_len = inf_len; udf_node->intern_free = data_space_avail; udf_node->intern_data = calloc(1, udf_node->intern_free); if (udf_node->intern_data) { memcpy(udf_node->intern_data, pos, inf_len); } else { error = ENOMEM; } if (dscrptr != given_dscrptr) free(dscrptr); return error; } icb_len = 0; switch (udf_node->addr_type) { case UDF_ICB_SHORT_ALLOC : icb_len = sizeof(struct short_ad); break; case UDF_ICB_LONG_ALLOC : icb_len = sizeof(struct long_ad); break; default : printf("UDF encountered an unknown allocation type %d\n", udf_node->addr_type); break; } udf_node->icb_len = icb_len; advance_sector = 1; while (icb_len && data_length) { switch (udf_node->addr_type) { case UDF_ICB_SHORT_ALLOC : s_ad = (struct short_ad *) pos; len = udf_rw32(s_ad->len); lb_num = udf_rw32(s_ad->lb_num); vpart_num = cur_alloc->vpart_num; break; case UDF_ICB_LONG_ALLOC : l_ad = (struct long_ad *) pos; len = udf_rw32(l_ad->len); lb_num = udf_rw32(l_ad->loc.lb_num); vpart_num = udf_rw16(l_ad->loc.part_num); if (l_ad->impl.im_used.flags & UDF_ADIMP_FLAGS_EXTENT_ERASED) { printf("UDF: got a `extent erased' flag in a file's long_ad; ignoring\n"); } break; default : printf("UDF encountered an unknown allocation type %d\n", udf_node->addr_type); break; } /* ecma-167 48.14.1.1 */ flags = (uint8_t) ((uint32_t) (len >> 30) & 3); len = len & ((1<<30)-1); if (flags == 3) { /* fill in next extent */ next_alloc->len = len; next_alloc->lb_num = lb_num; next_alloc->vpart_num = vpart_num; advance_sector = 0; /* don't advance to next sector */ icb_len = data_length; /* must be last */ printf("Continuing extent flagged at vpart = %d, lb_num = %d, len = %d\n", (int) vpart_num, (int) lb_num, (int) len); } else { if (len) { alloc_entry = calloc(1, sizeof(struct udf_allocentry)); if (!alloc_entry) { if (dscrptr != given_dscrptr) free(dscrptr); return ENOMEM; } alloc_entry->len = len; alloc_entry->lb_num = lb_num; alloc_entry->vpart_num = vpart_num; alloc_entry->flags = flags; TAILQ_INSERT_TAIL(&udf_node->alloc_entries, alloc_entry, next_alloc); } calculated_len += len; } data_length -= icb_len; pos += icb_len; } /* while */ if (advance_sector) { /* Note: UDF descriptor length is maximised to one sector */ next_alloc->lb_num++; /* advance one sector */ entries--; } } /* indirect ICB check */ if (entries) { /* load in new dscrptr */ if (dscrptr != given_dscrptr) free(dscrptr); error = udf_read_logvol_descriptor(udf_log_vol, next_alloc->vpart_num, next_alloc->lb_num, what, &dscrptr, NULL); } } while (entries && !error); /* error from reading next sector in extent is not considered an error */ if (dscrptr != given_dscrptr && dscrptr) free(dscrptr); if (calculated_len != udf_node->stat.st_size) { printf("UDF: create node length mismatch; stated as %g but calculated as %g bytes. fixing\n", (double) udf_node->stat.st_size, (double) calculated_len); udf_node->stat.st_size = calculated_len; } return 0; } int udf_readin_udf_node(struct udf_node *dir_node, struct fileid_desc *fid, struct udf_node **res_sub_node) { struct udf_node *sub_node; char *fid_name; char entry_name[NAME_MAX]; uint32_t bucket; ino_t hashkey; int error; /* check if its allready in the logical volume's udf_node cache (inodes) */ hashkey = udf_calc_hash(&fid->icb); bucket = hashkey & UDF_INODE_HASHMASK; LIST_FOREACH(sub_node, &dir_node->udf_log_vol->udf_nodes[bucket], next_node) { if (sub_node->hashkey == hashkey) { *res_sub_node = sub_node; DEBUG(printf("found node in hashlist\n")); return 0; } } /* dump the FID we are trying to read in */ UDF_VERBOSE_MAX(udf_dump_descriptor((union dscrptr *) fid)); fid_name = (char *) fid->data + udf_rw16(fid->l_iu); udf_to_unix_name(entry_name, fid_name, fid->l_fi, &dir_node->udf_log_vol->log_vol->desc_charset); /* build missing vnode */ error = udf_readin_anon_udf_node(dir_node->udf_log_vol, NULL, &fid->icb, entry_name, &sub_node); if (error) return error; if (!sub_node) { printf("sub_node = NULL? and no error? \n"); } assert(sub_node); /* link this UDF node to the mountpoint and remember its hash-key */ sub_node->mountpoint = dir_node->mountpoint; sub_node->hashkey = hashkey; /* XXX use file version number and filechar from fid ? XXX */ sub_node->file_version_num = udf_rw16(fid->file_version_num); /* user set */ sub_node->udf_filechar = fid->file_char; /* insert/replace in mountpoint's udfnode hashtable with optional check for doubles */ if (0) { struct udf_node *lookup; LIST_FOREACH(lookup, &dir_node->udf_log_vol->udf_nodes[bucket], next_node) { if (lookup->hashkey == sub_node->hashkey) printf("DOUBLE hashnode?\n"); } } LIST_INSERT_HEAD(&dir_node->udf_log_vol->udf_nodes[bucket], sub_node, next_node); DEBUG(printf("inserting hash node for hash = %d\n", (int) hashkey)); *res_sub_node = sub_node; return 0; } void udf_syncnode_callback(int reason, struct udf_wrcallback *wrcallback, int error, uint8_t *sectordata) { } /* XXX This mark node dirty code will simplify if we store nodes in buffers? XXX */ void udf_node_mark_dirty(struct udf_node *udf_node) { struct udf_allocentry *alloc_entry, *my_entry, *tail_entry; struct udf_node *search_node, *tail_node; if (udf_node->dirty) return; my_entry = TAILQ_FIRST(&udf_node->dscr_allocs); assert(my_entry); /* dscr locks ? */ UDF_MUTEX_LOCK(&udf_node->udf_log_vol->dirty_nodes_mutex); if (1) { /* insertion sort :-S */ tail_node = TAILQ_LAST(&udf_node->udf_log_vol->dirty_nodes, udf_node_list); if (!tail_node) { TAILQ_INSERT_TAIL(&udf_node->udf_log_vol->dirty_nodes, udf_node, next_dirty); } else { tail_entry = TAILQ_FIRST(&tail_node->dscr_allocs); if (tail_entry->lb_num < my_entry->lb_num) { TAILQ_INSERT_TAIL(&udf_node->udf_log_vol->dirty_nodes, udf_node, next_dirty); } else { /* find my place; could be done smarter */ TAILQ_FOREACH(search_node, &udf_node->udf_log_vol->dirty_nodes, next_dirty) { alloc_entry = TAILQ_FIRST(&tail_node->dscr_allocs); if (alloc_entry->lb_num > my_entry->lb_num) { TAILQ_INSERT_BEFORE(search_node, udf_node, next_dirty); break; /* foreach */ } } } } } else { /* dumb */ TAILQ_INSERT_TAIL(&udf_node->udf_log_vol->dirty_nodes, udf_node, next_dirty); } UDF_MUTEX_UNLOCK(&udf_node->udf_log_vol->dirty_nodes_mutex); udf_node->dirty = 1; } static void udf_node_unmark_dirty(struct udf_node *udf_node) { if (!udf_node->dirty) return; /* remove me just in case; why were we called otherwise? */ UDF_MUTEX_LOCK(&udf_node->udf_log_vol->dirty_nodes_mutex); TAILQ_REMOVE(&udf_node->udf_log_vol->dirty_nodes, udf_node, next_dirty); UDF_MUTEX_UNLOCK(&udf_node->udf_log_vol->dirty_nodes_mutex); udf_node->dirty = 0; } /* VOP_FSYNC : data FDATASYNC */ int udf_sync_udf_node(struct udf_node *udf_node, char *why) { DEBUG(printf("Syncing udf_node `%"PRIu64"` because of %s\n", udf_node->unique_id, why)); DEBUG(printf("sync udf node: dirty = %d, v_numoutput = %d\n", udf_node->dirty, udf_node->v_numoutput)); if (!udf_node->dirty) { /* Not dirty??!!! */ udf_node_unmark_dirty(udf_node); return 0; } if (!udf_node->udf_log_vol->writable) { fprintf(stderr, "encountered a dirty node on a read-only filingsystem!\n"); exit(1); } /* * We are really syncing disc but we are only continueing when the * node itself is clean... not breaking the semantics. */ /* XXX flushall flag magic XXX */ udf_bufcache->flushall = 1; udf_purgethread_kick("Sync node"); fflush(stdout); /* wait until all dirty bufs associated with this node are processed */ if (!udf_node->dirty) return 0; if (udf_node->v_numoutput) { usleep(100); } if (!udf_node->v_numoutput) return 0; UDF_VERBOSE(printf("(wait on node)")); while (udf_node->v_numoutput) { usleep(100); } return 0; } extern void udf_merge_allocentry_queue(struct udf_alloc_entries *queue, uint32_t lb_size); /* VOP_FSYNC : metadata FFILESYNC */ /* writeout the udf_node back to disc */ /* TODO dont forget to writeout our extended attributes and write out the link to the associated streamdir as well */ int udf_writeout_udf_node(struct udf_node *udf_node, char *why) { struct udf_wrcallback wr_callback; struct udf_allocentry *dscr_entry, *next_dscr_entry, *alloc_entry; union dscrptr *dscrptr; struct file_entry *fe; struct extfile_entry *efe; struct alloc_ext_entry *aee; struct lb_addr parent_icb; struct icb_tag *icbtag; struct long_ad *l_ad; struct short_ad *s_ad; uint32_t *l_adptr; /* points to length of alloc. descr. */ uint8_t *pos; char *what; uint32_t alloc_entries; uint64_t rest; /* in bytes */ uint64_t len; uint64_t logblks_rec; uint32_t lb_size, descr_ver; if (!udf_node->udf_log_vol->writable) { fprintf(stderr, "encountered a dirty node on a read-only filingsystem!\n"); exit(1); } lb_size = udf_node->udf_log_vol->lb_size; /* assure all file data is written out! and clean up */ udf_sync_udf_node(udf_node, why); /* XXX node lock? XXX */ UDF_MUTEX_LOCK(&udf_node->alloc_mutex); /* clean up allocentry queue so we get a nice clean layout */ udf_merge_allocentry_queue(&udf_node->alloc_entries, lb_size); UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); /* allocate for descriptor */ dscrptr = calloc(1, lb_size); /* not a bit biggish? */ if (!dscrptr) return ENOMEM; /* calculate logical blocks recorded; zero for interns [UDF 2.3.6.5, ECMA 4/14.9.11, 4/14.6.8] */ logblks_rec = 0; if (udf_node->addr_type != UDF_ICB_INTERN_ALLOC) { UDF_MUTEX_LOCK(&udf_node->alloc_mutex); TAILQ_FOREACH(alloc_entry, &udf_node->alloc_entries, next_alloc) { if (alloc_entry->flags == UDF_SPACE_ALLOCATED) { logblks_rec += (alloc_entry->len + lb_size-1) / lb_size; } } UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); } /* fill in (ext)fentry descriptor */ /* copy descriptor version from the logvol's */ bzero(&parent_icb, sizeof(struct lb_addr)); descr_ver = udf_rw16(udf_node->udf_log_vol->log_vol->tag.descriptor_ver); if (descr_ver == 3) { efe = &dscrptr->efe; efe->tag.id = udf_rw16(TAGID_EXTFENTRY); efe->tag.descriptor_ver = udf_rw16(descr_ver); efe->ckpoint = udf_rw32(1); /* [ECMA 4/14.17.17] */ udf_set_imp_id(&efe->imp_id); efe->logblks_rec = udf_rw64(logblks_rec); /* set additional fileinfo (access, uid/gid) etc. */ udf_node_set_fileinfo(udf_node, dscrptr); efe->obj_size = efe->inf_len; /* not true if there are streams [ECMA 4/48.17.11] */ icbtag = &efe->icbtag; } else if (descr_ver == 2) { fe = &dscrptr->fe; fe->tag.id = udf_rw16(TAGID_FENTRY); fe->tag.descriptor_ver = udf_rw16(descr_ver); fe->ckpoint = udf_rw32(1); /* [ECMA 4/14.17.17] */ udf_set_imp_id(&fe->imp_id); fe->logblks_rec = udf_rw64(logblks_rec); /* set additional fileinfo (access, uid/gid) etc. */ udf_node_set_fileinfo(udf_node, dscrptr); /* fe->obj_size doesn't exist */ icbtag = &fe->icbtag; } else { printf("UDF: i dont know this descriptor version %d\n", descr_ver); return EBADF; } /* update derived info */ udf_node->udf_icbtag_flags = (udf_node->udf_icbtag_flags & ~UDF_ICB_TAG_FLAGS_ALLOC_MASK) | udf_node->addr_type; /* strategy 4 has no parent nodes */ /* XXX NO extended attributes recorded YET (!!) XXX (like device nodes !!! ) */ dscr_entry = TAILQ_FIRST(&udf_node->dscr_allocs); /* ensure allocation of (ext) file descriptor */ if (!dscr_entry) { /* have to allocate one and add to queue ! */ printf("UDF: XXX no allocation of file descriptor entry yet in sync_udf_node\n"); return ENOENT; } /* XXX implement alloc entry walker? XXX */ UDF_MUTEX_LOCK(&udf_node->alloc_mutex); what = "UDF sync: File descriptor"; alloc_entry = TAILQ_FIRST(&udf_node->alloc_entries); do { assert(dscr_entry->len <= lb_size); if (icbtag) { /* fill in ICB fields */ /* XXX we're only writing out strategy type 4 !!! XXX */ icbtag->prev_num_dirs = udf_rw32(0); /* prev_alloc_entries = 0 due to strategy type 4 XXX */ icbtag->strat_type = udf_rw16(4); /* default UDF strategy type 4 XXX what about 4096? */ icbtag->parent_icb = parent_icb; icbtag->file_type = udf_node->udf_filetype; icbtag->flags = udf_rw16(udf_node->udf_icbtag_flags); icbtag->max_num_entries = udf_rw16(1); /* due to strategy type 4 ! XXX */ } pos = 0; rest = 0; l_adptr = NULL; /* invalid */ switch (udf_rw16(dscrptr->tag.id)) { case TAGID_FENTRY : /* not used now */ fe = &dscrptr->fe; pos = &fe->data[0] + udf_rw32(fe->l_ea); rest = dscr_entry->len - sizeof(struct file_entry) - udf_rw32(fe->l_ea); l_adptr = &fe->l_ad; break; case TAGID_EXTFENTRY : efe = &dscrptr->efe; pos = &efe->data[0] + udf_rw32(efe->l_ea); rest = dscr_entry->len - sizeof(struct extfile_entry) - udf_rw32(efe->l_ea); l_adptr = &efe->l_ad; break; case TAGID_ALLOCEXTENT : aee = &dscrptr->aee; pos = &aee->data[0]; rest = dscr_entry->len - sizeof(struct alloc_ext_entry); l_adptr = &aee->l_ad; break; case TAGID_INDIRECT_ENTRY : /* do we even do these in strat 4 ? */ printf("UDF: sanity check; request for writeout of indirect entry\n"); free(dscrptr); UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); return ENOENT; /* panic really */ } assert(l_adptr); /* fill in remaining allocation entries */ /* remember to keep one SPARE for extending */ alloc_entries = 0; DEBUG(printf("Allocated at ")); if (udf_node->addr_type == UDF_ICB_INTERN_ALLOC) { /* internal allocation -> copy in data */ assert(udf_node->intern_len <= rest); bzero(pos, rest); memcpy(pos, udf_node->intern_data, udf_node->intern_len); /* make sure the length for allocation entries is the same as the data it contains (4/8.8.2, 4/14.6.8) */ *l_adptr = udf_rw32(udf_node->intern_len); /* alloc_entry is zero, so it'll fall trough */ pos += udf_node->intern_len; /* advance pos for length calculation */ alloc_entry = NULL; /* not applicable */ } while ((rest > 2*udf_node->icb_len) && alloc_entry) { assert(udf_node->icb_len); DEBUG(printf("[%p, lb_num = %d, len = %d] ", alloc_entry, (uint32_t) alloc_entry->lb_num, (uint32_t) alloc_entry->len)); /* XXX assumption: UDF_SPACE_* is equal to UDF flags XXX */ len = alloc_entry->len | (((uint32_t) alloc_entry->flags) << 30); switch (udf_node->addr_type) { case UDF_ICB_SHORT_ALLOC : s_ad = (struct short_ad *) pos; s_ad->len = udf_rw32(len); s_ad->lb_num = udf_rw32(alloc_entry->lb_num); assert(alloc_entry->vpart_num == 0); if (alloc_entry->len == 0) s_ad->lb_num = udf_rw32(0); if (alloc_entry->flags == UDF_SPACE_FREE) s_ad->lb_num = udf_rw32(0); break; case UDF_ICB_LONG_ALLOC : l_ad = (struct long_ad *) pos; l_ad->len = udf_rw32(len); l_ad->loc.lb_num = udf_rw32(alloc_entry->lb_num); l_ad->loc.part_num = udf_rw16(alloc_entry->vpart_num); l_ad->impl.im_used.unique_id = udf_rw64(udf_node->unique_id); if (alloc_entry->len == 0) l_ad->loc.lb_num = udf_rw32(0); if (alloc_entry->flags == UDF_SPACE_FREE) l_ad->loc.lb_num = udf_rw32(0); break; } alloc_entries++; *l_adptr = udf_rw32(alloc_entries * udf_node->icb_len); alloc_entry = TAILQ_NEXT(alloc_entry, next_alloc); pos += udf_node->icb_len; rest -= udf_node->icb_len; } DEBUG(printf("\nend alloc\n\n")); next_dscr_entry = TAILQ_NEXT(dscr_entry, next_alloc); if (alloc_entry) { /* overflow */ /* ensure allocation of (ext) file descriptor */ if (!next_dscr_entry) { /* have to allocate one and add to queue ! */ printf("UDF: XXX no allocation of allocation extent descriptor yet in sync_udf_node\n"); UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); return ENOENT; } /* flag next extent being specified */ len = next_dscr_entry->len | ((uint32_t) UDF_SPACE_REDIRECT << 30); switch (udf_node->addr_type) { case UDF_ICB_SHORT_ALLOC : s_ad = (struct short_ad *) pos; s_ad->len = udf_rw32(len); s_ad->lb_num = udf_rw32(next_dscr_entry->lb_num); assert(next_dscr_entry->vpart_num == 0); break; case UDF_ICB_LONG_ALLOC : l_ad = (struct long_ad *) pos; l_ad->len = udf_rw32(len); l_ad->loc.lb_num = udf_rw32(next_dscr_entry->lb_num); l_ad->loc.part_num = udf_rw16(next_dscr_entry->vpart_num); l_ad->impl.im_used.unique_id = udf_rw64(udf_node->unique_id); break; } alloc_entries++; pos += udf_node->icb_len; rest -= udf_node->icb_len; } /* manage lengths */ dscrptr->tag.desc_crc_len = udf_rw16((pos - (uint8_t *) dscrptr) - UDF_DESC_TAG_LENGTH); /* writeout */ wr_callback.function = udf_syncnode_callback; #if 0 wr_callback.structure = (void *) udf_node; wr_callback.vpart_num = dscr_entry->vpart_num; wr_callback.lb_num = dscr_entry->lb_num; wr_callback.offset = 0; /* ? */ wr_callback.length = dscr_entry->len; #endif UDF_VERBOSE_MAX( udf_validate_tag_and_crc_sums(dscrptr); /* for dumping */ udf_dump_descriptor(dscrptr); ); errno = udf_write_logvol_descriptor(udf_node->udf_log_vol, dscr_entry->vpart_num, dscr_entry->lb_num, what, dscrptr, &wr_callback); /* advance */ if (alloc_entry) { dscr_entry = next_dscr_entry; assert(dscr_entry); /* allocate and initialise new allocation extent descriptor */ /* also flag no icbtag follows */ bzero(dscrptr, lb_size); aee = &dscrptr->aee; aee->tag.id = udf_rw16(TAGID_ALLOCEXTENT); aee->tag.descriptor_ver = udf_node->udf_log_vol->log_vol->tag.descriptor_ver; icbtag = NULL; what = "UDF sync: allocation extent"; } } while (alloc_entry); UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); /* XXX Free extra non used allocation extent descriptors XXX */ /* Mark me clean */ if (udf_node->mountpoint) { /* remove me just in case; why were we called otherwise? */ udf_node_unmark_dirty(udf_node); } return 0; } /****************************************************************************************** * * Space bitmap reader and writer * ******************************************************************************************/ /* tested OK */ int udf_read_in_space_bitmap(struct udf_alloc_entries *queue, struct space_bitmap_desc *sbd, uint32_t lb_size, uint64_t *freespace) { struct udf_allocentry *alloc_entry; uint64_t bits, from, now, start, end; uint8_t byte, bit, bitpos, state, *pos; int cnt; assert(udf_rw16(sbd->tag.id) == TAGID_SPACE_BITMAP); DEBUG(printf("processing space bitmap : \n");); bits = udf_rw32(sbd->num_bits); /* * Mark the disc as completely full; * Bugallert: use `udf_mark_allocentry_queue()' for the extent might * not fit in just one alloc entry for bigger discs */ assert(TAILQ_EMPTY(queue)); udf_mark_allocentry_queue(queue, lb_size, 0, bits * lb_size, UDF_SPACE_ALLOCATED, NULL, NULL); pos = sbd->data; from = 0; now = 0; bitpos = 0; byte = *pos; state = byte & 1; *freespace = 0; while (now < bits) { if (bitpos == 0) { byte = *pos++; } bit = byte & 1; if (bit != state) { if (state) { start = from; end = now-1; /* printf("[%08d - %08d]", start, end); */ udf_mark_allocentry_queue(queue, lb_size, start*lb_size, (end-start+1)*lb_size, UDF_SPACE_FREE, NULL, NULL); *freespace += (end-start+1)*lb_size; } from = now; state = bit; } byte >>= 1; bitpos = (bitpos+1) & 7; now++; } if (state) { start = from; end = now; /* printf("[%08d - %08d]", start, end); */ udf_mark_allocentry_queue(queue, lb_size, start*lb_size, (end-start)*lb_size, UDF_SPACE_FREE, NULL, NULL); *freespace += (end-start)*lb_size; } UDF_VERBOSE_TABLES( printf("\t\tFree space found on this partition"); cnt = 0; start = 0; TAILQ_FOREACH(alloc_entry, queue, next_alloc) { if (alloc_entry->flags == 0) { /* printf("... "); */ } else { if (cnt == 0) printf("\n\t\t\t"); printf("[%08"PRIu64" - %08"PRIu64"] ", start / lb_size, ((start + alloc_entry->len) / lb_size)-1); cnt++; if (cnt > 4) cnt = 0; } start += alloc_entry->len; } printf("\n"); ); /* merge is not nessisary */ return 0; } /* inverse of readin space bitmap; it synchronises the bitmap with the queue */ /* tested OK */ int udf_sync_space_bitmap(struct udf_alloc_entries *queue, struct space_bitmap_desc *sbd, uint32_t lb_size) { struct udf_allocentry *alloc_entry; uint32_t start, bits, total_bits; uint32_t cnt, byte; uint8_t bit, bitmask, setting; uint8_t *pos; /* merge it just in case */ udf_merge_allocentry_queue(queue, lb_size); total_bits = udf_rw32(sbd->num_bits); DEBUG(printf("SYNC SPACE BITMAP DEBUG: total bits = %d\n", total_bits)); alloc_entry = TAILQ_FIRST(queue); start = alloc_entry->lb_num; assert(start == 0); TAILQ_FOREACH(alloc_entry, queue, next_alloc) { DEBUG(printf(" [%d : %d + %d]", alloc_entry->flags, alloc_entry->lb_num, alloc_entry->len)); bits = alloc_entry->len / lb_size; assert(bits*lb_size == alloc_entry->len); byte = start / 8; bit = start - byte*8; pos = sbd->data + byte; if (byte*8 + bit + bits > total_bits) { /* XXX > or >= ? */ /* this should NEVER happen */ printf("UDF: not enough space writing back space bitmap! HELP!\n"); return EBADF; } cnt = 0; setting = (alloc_entry->flags != UDF_SPACE_FREE) ? 0 : 255; while (cnt < bits) { bitmask = (1 << bit); /* simple sanity check */ if (byte*8 + bit >= total_bits) { printf("IEEEE!!!! too big; %d instead of %d\n", (byte*8 + bit), total_bits); } *pos = (*pos & (~bitmask)) | (setting ? bitmask: 0); cnt++; bit++; if (bit == 8) { /* byte transition */ byte++; bit = 0; pos++; #if 0 /* speedup by doing bytes at a time */ while (bits-cnt > 8) { *pos = setting; cnt += 8; pos++; byte++; } #endif } } start += bits; } DEBUG(printf("\n\n")); return 0; } /****************************************************************************************** * * Filepart readers and writers * ******************************************************************************************/ /* part of VOP_STRATEGY */ /* internal function; reads in a new buffer */ /* !!! bufcache lock ought to be held on entry !!! */ int udf_readin_file_buffer(struct udf_node *udf_node, char *what, uint32_t sector, int cache_flags, struct udf_buf **buf_entry_p) { struct udf_allocentry *alloc_entry; struct udf_buf *buf_entry; uint64_t cur_offset; uint64_t overlap_length, overlap_sectors, transfer_length; uint32_t lb_size; uint32_t len, lb_num, vpart_num; int32_t error; uint8_t flags; ino_t hashkey; assert(udf_node); assert(buf_entry_p); assert(udf_bufcache->bufcache_lock.locked); error = udf_get_buf_entry(udf_node, buf_entry_p); if (error) return error; buf_entry = *buf_entry_p; lb_size = udf_node->udf_log_vol->lb_size; /* internal node? */ if (udf_node->addr_type == UDF_ICB_INTERN_ALLOC) { buf_entry->b_lblk = 0; buf_entry->b_flags = 0; /* not dirty, not needing alloc */ buf_entry->b_bcount = udf_node->intern_len; buf_entry->b_resid = lb_size - udf_node->intern_len; memcpy(buf_entry->b_data, udf_node->intern_data, udf_node->intern_len); UDF_MUTEX_LOCK(&udf_node->buf_mutex); udf_attach_buf_to_node(udf_node, buf_entry); UDF_MUTEX_UNLOCK(&udf_node->buf_mutex); return error; } /* `normal' node */ /* find sector in the allocation space */ UDF_MUTEX_LOCK(&udf_node->alloc_mutex); cur_offset = 0; error = EIO; /* until proven readable */ TAILQ_FOREACH(alloc_entry, &udf_node->alloc_entries, next_alloc) { len = alloc_entry->len; lb_num = alloc_entry->lb_num; vpart_num = alloc_entry->vpart_num; flags = alloc_entry->flags; /* check overlap with this alloc entry */ if (cur_offset + len > sector * lb_size) { /* found an extent that fits */ assert(((sector * lb_size - cur_offset) % lb_size) == 0); /* ought to be on sector boundary */ lb_num += sector - (cur_offset / lb_size); overlap_length = cur_offset + len - sector * lb_size; overlap_sectors = (overlap_length + lb_size -1) / lb_size; transfer_length = MIN(lb_size, overlap_length); /* max one logical sector */ /* fill in new buf info */ buf_entry->b_lblk = sector; buf_entry->b_bcount = transfer_length; buf_entry->b_resid = lb_size - transfer_length; /* sector transfered; mark valid */ buf_entry->b_flags = 0; /* not dirty and valid */ switch (flags) { case UDF_SPACE_ALLOCATED : /* on disc or in the write queue/cache to be written out; read one block at a time */ error = udf_read_logvol_sector(udf_node->udf_log_vol, vpart_num, lb_num, what, buf_entry->b_data, overlap_sectors, cache_flags); break; case UDF_SPACE_FREE : /* fall trough */ case UDF_SPACE_ALLOCATED_BUT_NOT_USED : error = 0; bzero(buf_entry->b_data, lb_size); break; default : fprintf(stderr, "Got an redirect flag, can't happen\n"); break; } if (error) { fprintf(stderr, "\tgot error from read_logvol_sector : %s\n", strerror(error)); break; /* FOREACH */ } UDF_MUTEX_LOCK(&udf_node->buf_mutex); udf_attach_buf_to_node(udf_node, buf_entry); UDF_MUTEX_UNLOCK(&udf_node->buf_mutex); UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); return 0; } /* looking for overlap */ cur_offset += len; } /* FOREACH */ UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); *buf_entry_p = NULL; udf_mark_buf_clean(udf_node, buf_entry); /* its destroyed so not dirty */ udf_mark_buf_allocated(udf_node, buf_entry); /* i.e. taken care of */ udf_free_buf_entry(buf_entry); return error; } /* internal function; shedule writing out of a buffer */ /* part of VOP_STRATEGY */ int udf_writeout_file_buffer(struct udf_node *udf_node, char *what, int cache_flags, struct udf_buf *buf_entry) { struct udf_allocentry *from_alloc, *to_alloc, *alloc_entry; uint32_t lb_num, lb_size, lblk; uint16_t vpart_num; int error; if (!udf_node->udf_log_vol->writable) { /* ieek */ fprintf(stderr, "write request from non writable file buffer?\n"); } /* first get logical block offset and block size */ lblk = buf_entry->b_lblk; lb_size = udf_node->udf_log_vol->lb_size; error = 0; UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock); UDF_MUTEX_LOCK(&udf_node->alloc_mutex); UDF_MUTEX_LOCK(&udf_node->buf_mutex); /* maybe a bit too strict locking; code is not winning the beauty contest */ /* check if we can convert/save disc space */ if (udf_node->stat.st_size <= udf_node->intern_free) { /* convert to internal alloc with backup storage (writing there) */ if (udf_node->addr_type != UDF_ICB_INTERN_ALLOC) { DEBUG(printf("CONVERTING to intern alloc\n")); /* first free all previously allocated sectors (if any) */ error = udf_node_release_extent(udf_node, 0, udf_node->stat.st_size); } assert(!error); if (!udf_node->intern_data) { udf_node->intern_data = calloc(1, udf_node->intern_free); } if (udf_node->intern_data) { assert(buf_entry->b_bcount <= udf_node->intern_free); memcpy(udf_node->intern_data, buf_entry->b_data, buf_entry->b_bcount); udf_node->intern_len = buf_entry->b_bcount; udf_node->addr_type = UDF_ICB_INTERN_ALLOC; /* we dont write here: mark buffer clean */ udf_mark_buf_clean(udf_node, buf_entry); udf_mark_buf_allocated(udf_node, buf_entry); /* signal its allocated */ buf_entry->b_flags &= ~(B_ERROR); /* check if all buffers are gone now and mark node for writeout to indicate state change */ assert(udf_node->v_numoutput == 0); udf_node_mark_dirty(udf_node); UDF_MUTEX_UNLOCK(&udf_node->buf_mutex); UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); return 0; } /* fall trough ... for some reason it isn't converted */ } else { if (udf_node->addr_type == UDF_ICB_INTERN_ALLOC) { DEBUG(printf("CONVERTING to normal alloc\n")); /* won't fit anymore, have to turn it into a `normal' alloced */ udf_node->intern_len = 0; if (udf_node->intern_data) free(udf_node->intern_data); udf_node->intern_data = NULL; udf_node->icb_len = sizeof(struct long_ad); udf_node->addr_type = UDF_ICB_LONG_ALLOC; udf_node_mark_dirty(udf_node); /* mark needalloc to make sure it gets an address */ udf_mark_buf_needing_allocate(udf_node, buf_entry); /* signal it needs allocation */ } } /* merge queue first to get better performance */ udf_merge_allocentry_queue(&udf_node->alloc_entries, lb_size); /* get extent that it represents */ udf_mark_allocentry_queue(&udf_node->alloc_entries, lb_size, lblk*lb_size, buf_entry->b_bcount, UDF_SPACE_ALLOCATED, &from_alloc, &to_alloc); alloc_entry = from_alloc; if (buf_entry->b_flags & B_NEEDALLOC) { /* need allocation */ error = udf_node_allocate_lbs(udf_node, /* num lb */ 1, &vpart_num, &lb_num, NULL); assert(!error); udf_mark_buf_allocated(udf_node, buf_entry); alloc_entry->lb_num = lb_num; alloc_entry->vpart_num = vpart_num; } assert(TAILQ_NEXT(alloc_entry, next_alloc) == to_alloc || (alloc_entry == to_alloc)); lb_num = alloc_entry->lb_num; vpart_num = alloc_entry->vpart_num; UDF_MUTEX_UNLOCK(&udf_node->buf_mutex); UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); /* printf("writing out filebuffer vpart %d, lb_num %d for %s\n", vpart_num, lb_num, what); */ error = udf_write_logvol_sector(udf_node->udf_log_vol, vpart_num, lb_num, "File contents", buf_entry->b_data, cache_flags, NULL); UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock); UDF_MUTEX_LOCK(&udf_node->buf_mutex); if (error) { printf("YIKES error during writing of logvol_sector\n"); udf_mark_buf_needing_allocate(udf_node, buf_entry); buf_entry->b_flags |= B_ERROR; /* buf_entry->b_errno = error; */ } else { udf_mark_buf_clean(udf_node, buf_entry); buf_entry->b_flags &= ~(B_ERROR); } UDF_MUTEX_UNLOCK(&udf_node->buf_mutex); UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); return error; } /* VOP_READ */ int udf_read_file_part_uio(struct udf_node *udf_node, char *what, int content, struct uio *data_uio) { struct udf_buf *buf_entry; uint32_t lb_size, sector; uint64_t offset, data_length; uint8_t *base; int error, short_buf; if (!udf_node) return EINVAL; /* NOTE: we are NOT marking the node dirty only by reading it */ udf_set_timespec_now(&udf_node->stat.st_atimespec); if (udf_node->stat.st_size == 0) { if (data_uio->uio_resid) return EIO; /* reading past end by default */ return 0; } lb_size = udf_node->udf_log_vol->lb_size; error = 0; while (data_uio->uio_resid) { error = 0; short_buf = 0; sector = data_uio->uio_offset / lb_size; /* lookup sector in buffer set */ UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock); udf_lookup_node_buf(udf_node, sector, &buf_entry); if (!buf_entry || (buf_entry && (buf_entry->b_lblk != sector))) { /* `page in' sector from file */ error = udf_readin_file_buffer(udf_node, what, sector, content, &buf_entry); } if (!error && buf_entry) { offset = data_uio->uio_offset - sector*lb_size; base = buf_entry->b_data; if (offset >= 0) { data_length = buf_entry->b_bcount - offset; data_length = MIN(data_length, data_uio->uio_resid); uiomove(base + offset, data_length, data_uio); } short_buf = (buf_entry->b_bcount < lb_size); /* short buf -> none will follow */ } assert(!error || (error && !buf_entry)); UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); if (error) break; /* while */ if (data_uio->uio_resid == 0) return 0; /* finished? */ if (short_buf) break; /* while */ } /* while */ if (data_uio->uio_resid) { printf("UDF: WARNING file is truncated; missing %d bytes while reading %s\n", (int) data_uio->uio_resid, what); return EIO; } return error; } /* XXX meaning is to be changed; kept for reference still XXX */ void udf_filepart_write_callback(int reason, struct udf_wrcallback *wrcallback, int error, uint8_t *sectordata) { } /* VOP_TRUNCATE */ /* doesn't lock udf_node */ int udf_truncate_node(struct udf_node *udf_node, uint64_t length /* ,ioflags */) { struct udf_log_vol *udf_log_vol; struct udf_allocentry *alloc_entry, *cut_point; struct udf_buf *buf_entry, *marker; uint32_t lb_size; uint64_t cur_extent, block_extent, new_extent, too_much; uint32_t last_sector; int32_t error; if (!udf_node) return EINVAL; if (udf_open_logvol(udf_node->udf_log_vol)) return EROFS; udf_log_vol = udf_node->udf_log_vol; lb_size = udf_log_vol->lb_size; /* we might change this node AND access times ... */ if (!udf_node->dirty) { /* mark node as dirty */ udf_node_mark_dirty(udf_node); } /* XXX lock node !!! XXX */ /* merge known allocation entry queue */ new_extent = length; block_extent = lb_size * ((new_extent + lb_size-1) / lb_size); /* inclusive */ too_much = block_extent - new_extent; assert(block_extent >= new_extent); UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock); UDF_MUTEX_LOCK(&udf_node->alloc_mutex); udf_merge_allocentry_queue(&udf_node->alloc_entries, lb_size); /* extend the file when nessisary */ if (new_extent > udf_node->stat.st_size) { if (udf_node->addr_type == UDF_ICB_INTERN_ALLOC) { /* grow intern node by converting it to a normal buffer first */ /* XXX to seperate function XXX */ error = udf_get_buf_entry(udf_node, &buf_entry); if (error) { UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); return error; } buf_entry->b_lblk = 0; buf_entry->b_flags = 0; buf_entry->b_bcount = MIN(lb_size, new_extent); buf_entry->b_resid = MAX(0, lb_size - new_extent); memcpy(buf_entry->b_data, udf_node->intern_data, udf_node->intern_len); UDF_MUTEX_LOCK(&udf_node->buf_mutex); udf_attach_buf_to_node(udf_node, buf_entry); udf_mark_buf_dirty(udf_node, buf_entry); UDF_MUTEX_UNLOCK(&udf_node->buf_mutex); udf_node->intern_len = 0; if (udf_node->intern_data) free(udf_node->intern_data); udf_node->intern_data = NULL; udf_node->icb_len = sizeof(struct long_ad); udf_node->addr_type = UDF_ICB_LONG_ALLOC; } udf_cut_allocentry_queue(&udf_node->alloc_entries, lb_size, block_extent); if (new_extent < block_extent) { alloc_entry = TAILQ_LAST(&udf_node->alloc_entries, udf_alloc_entries); assert(alloc_entry->len > too_much); alloc_entry->len -= too_much; } udf_node->stat.st_size = new_extent; } UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); /* `free' the extent when shrinking the extent */ if (new_extent < udf_node->stat.st_size) { DEBUG(printf("TRIMMING file %"PRIu64" from %d to %d\n", udf_node->unique_id, (int32_t) udf_node->stat.st_size, (uint32_t) length)); /* free file buffers that are not needed anymore */ /* XXX NetBSD kernel : uvm_vnp_setsize() XXX */ marker = calloc(1, sizeof(struct udf_buf)); if (!marker) return ENOMEM; UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock); UDF_MUTEX_LOCK(&udf_node->buf_mutex); /* use marker as we are going to delete items in the list we are traversing */ last_sector = udf_node->stat.st_size / lb_size; TAILQ_INSERT_HEAD(&udf_node->vn_bufs, marker, b_vnbufs); while ((buf_entry = TAILQ_NEXT(marker, b_vnbufs))) { /* advance marker */ TAILQ_REMOVE(&udf_node->vn_bufs, marker, b_vnbufs); TAILQ_INSERT_AFTER(&udf_node->vn_bufs, buf_entry, marker, b_vnbufs); /* process buf_entry */ if (buf_entry->b_lblk > last_sector) { udf_mark_buf_clean(udf_node, buf_entry); /* its destroyed so not dirty */ udf_mark_buf_allocated(udf_node, buf_entry); /* i.e. taken care of */ udf_detach_buf_from_node(udf_node, buf_entry); udf_free_buf_entry(buf_entry); } } TAILQ_REMOVE(&udf_node->vn_bufs, marker, b_vnbufs); free(marker); /* trim last buffer entry */ /* XXX NetBSD kernel : uvm_vnp_zerorange(), vtruncbuf() XXX */ buf_entry = TAILQ_LAST(&udf_node->vn_bufs, udf_buf_queue); if (buf_entry) { buf_entry->b_bcount = udf_node->stat.st_size % lb_size; buf_entry->b_resid = buf_entry->b_bufsize - buf_entry->b_bcount; /* still data to record? */ if (buf_entry->b_bcount == 0) { /* marker not an issue here */ udf_mark_buf_clean(udf_node, buf_entry); /* its destroyed so not dirty */ udf_mark_buf_allocated(udf_node, buf_entry); /* i.e. taken care of */ udf_detach_buf_from_node(udf_node, buf_entry); udf_free_buf_entry(buf_entry); } } UDF_MUTEX_UNLOCK(&udf_node->buf_mutex); UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); UDF_MUTEX_LOCK(&udf_node->alloc_mutex); if (udf_node->addr_type == UDF_ICB_INTERN_ALLOC) { /* shrink intern node */ too_much = udf_node->stat.st_size - new_extent; udf_node->intern_len -= too_much; udf_node->intern_free += too_much; memset(udf_node->intern_data + new_extent, 0, too_much); } else { /* shrink alloc entries _after_ deleting bufs to make sure we are not crossed */ error = udf_node_release_extent(udf_node, block_extent, udf_node->stat.st_size); /* after `cleanup', trim now unused entries */ udf_merge_allocentry_queue(&udf_node->alloc_entries, lb_size); if (new_extent == 0) { /* remove all; most common case too */ while ((alloc_entry = TAILQ_FIRST(&udf_node->alloc_entries))) { TAILQ_REMOVE(&udf_node->alloc_entries, alloc_entry, next_alloc); free(alloc_entry); } cur_extent = 0; } else { /* move code to udf_allocentries.c ? */ udf_cut_allocentry_queue(&udf_node->alloc_entries, lb_size, block_extent); /* find cut-point */ cur_extent = 0; TAILQ_FOREACH(alloc_entry, &udf_node->alloc_entries, next_alloc) { cur_extent += alloc_entry->len; if (cur_extent == block_extent) break; } cut_point = alloc_entry; /* all after this point need to be deleted */ assert(cut_point); while ((alloc_entry = TAILQ_NEXT(cut_point, next_alloc))) { TAILQ_REMOVE(&udf_node->alloc_entries, alloc_entry, next_alloc); free(alloc_entry); } /* clip last entry to the correct size */ if (new_extent < block_extent) { assert(too_much == block_extent - new_extent); alloc_entry = TAILQ_LAST(&udf_node->alloc_entries, udf_alloc_entries); assert(alloc_entry->len > too_much); alloc_entry->len -= too_much; cur_extent -= too_much; } } assert(cur_extent == new_extent); } udf_node->stat.st_size = new_extent; UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); } return 0; } /* VOP_WRITE */ int udf_write_file_part_uio(struct udf_node *udf_node, char *what, int content, struct uio *data_uio) { struct udf_buf *buf_entry; uint8_t *base; uint64_t offset, lb_size; uint64_t sector, data_length; uint64_t new_possible_extent; uint64_t start_extent, end_extent; int error, appending, allocated; if (!udf_node) return EINVAL; if (udf_open_logvol(udf_node->udf_log_vol)) return EROFS; /* write chanches both modification and file status times */ udf_set_timespec_now(&udf_node->stat.st_ctimespec); udf_set_timespec_now(&udf_node->stat.st_mtimespec); /* Zero length write -> finished */ if (data_uio->uio_resid == 0) return 0; /* TODO lock node directly to avoid multiple writers entering */ /* pthread_rwlock_wrlock(&udf_node->udf_node_lock); */ if (!udf_node->dirty) { udf_node_mark_dirty(udf_node); } /* auto-extent file */ appending = 0; allocated = 0; new_possible_extent = data_uio->uio_offset + data_uio->uio_resid; if (new_possible_extent >= udf_node->stat.st_size) { /* BUGALERT: extra space can be pre-allocated AFTER file length */ error = udf_truncate_node(udf_node, new_possible_extent); appending = 1; } lb_size = udf_node->udf_log_vol->lb_size; while (data_uio->uio_resid) { error = 0; sector = data_uio->uio_offset / lb_size; /* lookup sector in buffer set */ UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock); udf_lookup_node_buf(udf_node, sector, &buf_entry); if (!buf_entry || (buf_entry && (buf_entry->b_lblk != sector))) { /* not found in cache; `page in' sector from file BUT ONLY if we don't completely overwrite it anyway */ if ((data_uio->uio_resid < lb_size) && (!appending)) { DEBUG(printf("Reading in file buffer for %s for %"PRIu64" bytes\n", what, (uint64_t) data_uio->uio_resid)); error = udf_readin_file_buffer(udf_node, what, sector, content, &buf_entry); } /* check if the extent is allocated; check assumption that the size of a buffer is a lb_size */ if (buf_entry) assert(buf_entry->b_bufsize == lb_size); UDF_MUTEX_LOCK(&udf_node->alloc_mutex); start_extent = lb_size * sector; end_extent = MIN(udf_node->stat.st_size, start_extent + lb_size); error = udf_extent_properties(&udf_node->alloc_entries, lb_size, start_extent, end_extent, &allocated); UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex); /* check free space if space is to allocated */ if (!buf_entry || !allocated) { /* be coulant on metadata here to avoid not to easy to solve resource problems */ if (content == UDF_C_USERDATA) { /* check for space no space anymore for userdata (bit on the safe side really) */ assert(udf_node->udf_log_vol); if (!udf_confirm_freespace(udf_node->udf_log_vol, content, lb_size)) { UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); return ENOSPC; } } } DEBUG(if (allocated) printf("Writing pre-allocated buffer\n")); if (!buf_entry) { /* create new buffer */ error = udf_get_buf_entry(udf_node, &buf_entry); if (error) { UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); return error; } /* dont forget to set the relative block number! */ buf_entry->b_lblk = sector; /* add it to the buffer list */ UDF_MUTEX_LOCK(&udf_node->buf_mutex); udf_attach_buf_to_node(udf_node, buf_entry); UDF_MUTEX_UNLOCK(&udf_node->buf_mutex); } assert(buf_entry); if (allocated) { /* * we could free the old allocated extent and mark it needing allocation * again */ } if (!allocated) { /* mark it needs to be allocated, locks are not very nice here */ UDF_MUTEX_LOCK(&udf_node->buf_mutex); udf_mark_buf_needing_allocate(udf_node, buf_entry); UDF_MUTEX_UNLOCK(&udf_node->buf_mutex); } } if (error) { UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); break; /* while */ } assert(buf_entry); offset = data_uio->uio_offset - sector*lb_size; base = buf_entry->b_data; assert(offset >= 0); if (offset >= 0) { UDF_MUTEX_LOCK(&udf_node->buf_mutex); udf_mark_buf_dirty(udf_node, buf_entry); data_length = buf_entry->b_bufsize - offset; data_length = MIN(data_length, data_uio->uio_resid); uiomove(base + offset, data_length, data_uio); buf_entry->b_bcount = MAX(buf_entry->b_bcount, offset + data_length); buf_entry->b_resid = buf_entry->b_bufsize - buf_entry->b_bcount; UDF_MUTEX_UNLOCK(&udf_node->buf_mutex); } UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock); if (data_uio->uio_resid == 0) return 0; /* finished? */ } /* while */ return 0; } /****************************************************************************************** * * UDF volume and descriptor logic * ******************************************************************************************/ struct udf_volumeset *udf_search_volumeset(char *volset_id) { struct udf_volumeset *volumeset; struct udf_pri_vol *primary; /* XXX this is a bit ugly XXX */ SLIST_FOREACH(volumeset, &udf_volumeset_list, next_volumeset) { primary = STAILQ_FIRST(&volumeset->primaries); assert(primary->pri_vol); if (memcmp(primary->pri_vol->volset_id, volset_id, 128) == 0) return volumeset; } return NULL; } struct udf_pri_vol *udf_search_primary(struct udf_volumeset *set, char *id) { struct udf_pri_vol *primary; STAILQ_FOREACH(primary, &set->primaries, next_primary) { assert(primary->pri_vol); if (memcmp(primary->pri_vol->vol_id, id, 32) == 0) return primary; } return NULL; } struct udf_log_vol *udf_search_logical_volume_in_primary(struct udf_pri_vol *primary, char *logvol_id) { struct udf_log_vol *here; SLIST_FOREACH(here, &primary->log_vols, next_logvol) { if (memcmp(here->log_vol->logvol_id, logvol_id, 128) == 0) return here; } return NULL; } /* called not very often ... ; free incomming when not needed */ int udf_proc_pri_vol(struct udf_session *udf_session, struct udf_pri_vol **current, struct pri_vol_desc *incomming) { struct udf_volumeset *volset; struct udf_pri_vol *primary; assert(current); volset = udf_search_volumeset(incomming->volset_id); if (!volset) { /* create a volume set */ volset = calloc(1, sizeof(struct udf_volumeset)); if (!volset) { free(incomming); return ENOMEM; } /* populate and link in */ volset->max_partnum = 0; STAILQ_INIT(&volset->primaries); SLIST_INSERT_HEAD(&udf_volumeset_list, volset, next_volumeset); } assert(volset); primary = udf_search_primary(volset, incomming->vol_id); *current = primary; if (!primary) { /* create a primary volume */ primary = calloc(1, sizeof(struct udf_pri_vol)); if (!primary) { free(incomming); return ENOMEM; } /* add to volset's primaries list */ STAILQ_INSERT_TAIL(&volset->primaries, primary, next_primary); *current = primary; } else { /* mark as current */ /* ok ... we now need to check if this new descriptor is a newer version */ if (udf_rw32(incomming->seq_num) <= udf_rw32(primary->pri_vol->seq_num)) { if (udf_session->session_num <= (*current)->udf_session->session_num) { DEBUG(printf("UDF: DISCARDING primary descriptor for its the same but higher session number\n")); /* its an older one; ignore */ free(incomming); return 0; } } DEBUG(printf("UPDATING primary descriptor for it has a higher session number\n")); } /* update the primary volume descriptor */ if (primary->pri_vol) free(primary->pri_vol); primary->volumeset = volset; primary->pri_vol = incomming; primary->udf_session = udf_session; return 0; } /* not called often ; free lvid when not used */ int udf_proc_logvol_integrity(struct udf_log_vol *udf_log_vol, struct logvol_int_desc *new_lvid) { struct udf_logvol_info *impl; uint64_t psize, pfree; uint32_t *free_space_pos, *size_pos; uint32_t lb_size; int error, tagid, part_map; error = udf_check_tag((union dscrptr *) new_lvid); if (error) { return error; /* return error on faulty tag */ } tagid = udf_rw16(new_lvid->tag.id); /* getting a terminator tag or zero is an OK condition */ if ((tagid == TAGID_TERM) || (tagid == 0)) { return 0; } /* not getting an logical volume itegrity volume descriptor is an error now */ if (tagid != TAGID_LOGVOL_INTEGRITY) { printf("IEE! got a %d tag while searching for a logical volume integrity descriptor\n", tagid); return EBADF; /* XXX error code? XXX */ } /* check CRC on the contents of the logvol integrity */ error = udf_check_tag_payload((union dscrptr *) new_lvid); if (error) { return error; } /* allways go for the next in line; silly but thats Ecma-167/UDF */ /* process information contained in logical volume integrity descriptor */ udf_log_vol->logvol_state = udf_rw32(new_lvid->integrity_type); udf_log_vol->integrity_serial = udf_rw16(new_lvid->tag.serial_num); impl = (struct udf_logvol_info *) (new_lvid->tables + 2*udf_rw32(new_lvid->num_part)); udf_log_vol->min_udf_readver = udf_rw16(impl->min_udf_readver); udf_log_vol->min_udf_writever = udf_rw16(impl->min_udf_writever); udf_log_vol->max_udf_writever = udf_rw16(impl->max_udf_writever); udf_log_vol->num_files = udf_rw32(impl->num_files); udf_log_vol->num_directories = udf_rw32(impl->num_directories); udf_log_vol->next_unique_id = udf_rw64(new_lvid->lvint_next_unique_id); /* calculate free space from this integrity descritor */ lb_size = udf_log_vol->lb_size; /* init start positions */ free_space_pos = &new_lvid->tables[0]; size_pos = &new_lvid->tables[udf_log_vol->num_part_mappings]; /* init counters */ udf_log_vol->total_space = udf_log_vol->free_space = udf_log_vol->await_alloc_space = 0; for (part_map = 0; part_map < udf_log_vol->num_part_mappings; part_map++) { psize = udf_rw32(*size_pos); size_pos++; pfree = udf_rw32(*free_space_pos); free_space_pos++; if (pfree != UINT_MAX) { /* if UINT_MAX, its not applicable like virtual space partitions */ udf_log_vol->total_space += (uint64_t) psize * lb_size; udf_log_vol->free_space += (uint64_t) pfree * lb_size; } } UDF_VERBOSE( if (udf_log_vol->logvol_state == UDF_INTEGRITY_OPEN) { udf_dump_timestamp("\t\t\t\tmarked open at ", &new_lvid->time); } else { udf_dump_timestamp("\t\t\t\tmarked closed at ", &new_lvid->time); } ); return 0; } void udf_derive_new_logvol_integrity(struct udf_log_vol *udf_log_vol) { udf_log_vol->logvol_state = UDF_INTEGRITY_OPEN; udf_log_vol->integrity_serial = 1; /* analyse the log vol to check out minimum and maximum read/write versions */ if (udf_rw16(udf_log_vol->log_vol->tag.descriptor_ver) == 2) { udf_log_vol->min_udf_readver = 0x0102; udf_log_vol->min_udf_writever = 0x0150; udf_log_vol->max_udf_writever = 0x0150; } else { udf_log_vol->min_udf_readver = 0x0201; udf_log_vol->min_udf_writever = 0x0201; udf_log_vol->max_udf_writever = 0x0201; /* 2.50? */ } udf_log_vol->num_files = 0; udf_log_vol->num_directories = 0; udf_log_vol->next_unique_id = 16; /* zero first, rest 15/16+ minimum */ } int udf_proc_logvol_integrity_sequence(struct udf_log_vol *udf_log_vol) { union dscrptr *dscr; uint32_t sector, length, lvid_len, num_sectors; uint32_t lb_size; int error; sector = udf_rw32(udf_log_vol->log_vol->integrity_seq_loc.loc); length = udf_rw32(udf_log_vol->log_vol->integrity_seq_loc.len); lb_size = udf_log_vol->lb_size; /* go for the default `open' integrity first as initialisation */ udf_derive_new_logvol_integrity(udf_log_vol); if (!length) { fprintf(stderr, "UDF: no volume integrity descriptor sequence space defined... OK for Ecma-167, not for UDF; rejecting\n"); return EBADF; } error = 0; while (length) { error = udf_read_session_descriptor(udf_log_vol->primary->udf_session, sector, "Logical volume integrity descriptor (LVID)", &dscr, &lvid_len); if (error) { if (dscr) free(dscr); dscr = NULL; break; } UDF_VERBOSE_MAX(udf_dump_descriptor(dscr)); error = udf_proc_logvol_integrity(udf_log_vol, &dscr->lvid); if (error) break; if (udf_rw16(dscr->tag.id) == TAGID_TERM) break; num_sectors = (lvid_len + lb_size-1) / lb_size; length -= num_sectors * lb_size; sector += num_sectors; if (udf_rw32(dscr->lvid.next_extent.len)) { sector = udf_rw32(dscr->lvid.next_extent.loc); length = udf_rw32(dscr->lvid.next_extent.len); } /* free consumed descriptor */ free(dscr); dscr = NULL; } /* free dangling descriptor */ if (dscr) free(dscr); /* either an error has occured or we have processed all descriptors */ if (error) { fprintf(stderr, "WARNING: integrity sequence ended with a bad descriptor; creating new\n"); udf_derive_new_logvol_integrity(udf_log_vol); return ENOENT; } /* * If its marked closed we hope/assume all is fine otherwise it may be * marked closed later on when we are using a VAT and its found and * correct */ return 0; } /* Add partition mapping to specified logvol descriptor */ void udf_add_physical_to_logvol(struct logvol_desc *logvol, uint16_t vol_seq_num, uint16_t phys_part_num) { union udf_pmap *pmap; uint8_t *pmap_pos; pmap_pos = logvol->maps + udf_rw32(logvol->mt_l); pmap = (union udf_pmap *) pmap_pos; pmap->pm1.type = 1; pmap->pm1.len = sizeof(struct part_map_1); pmap->pm1.vol_seq_num = udf_rw16(vol_seq_num); pmap->pm1.part_num = udf_rw16(phys_part_num); /* increment partition mapping count */ logvol->n_pm = udf_rw32(udf_rw32(logvol->n_pm) + 1); logvol->mt_l = udf_rw32(udf_rw32(logvol->mt_l) + sizeof(struct part_map_1)); logvol->tag.desc_crc_len = udf_rw16(udf_rw16(logvol->tag.desc_crc_len) + sizeof(struct part_map_1)); } #if 0 /* not yet */ void udf_add_sparable_to_logvol(struct logvol_desc *logvol, uint16_t vol_seq_num, uint16_t phys_part_num, uint16_t packet_len, ) { union udf_pmap *pmap; uint8_t *pmap_pos; pmap_pos = logvol->maps + udf_rw32(logvol->mt_l); pmap = (union udf_pmap *) pmap_pos; pmap->pm1.type = 1; pmap->pm1.len = sizeof(struct part_map_1); pmap->pm1.vol_seq_num = vol_seq_num; pmap->pm1.part_num = phys_part_num; /* increment partition mapping count */ logvol->n_pm = udf_rw32(udf_rw32(logvol->n_pm) + 1); logvol->mt_l = udf_rw32(udf_rw32(logvol->mt_l) + sizeof(struct part_map_1)); logvol->tag.desc_crc_len = udf_rw16(udf_rw16(logvol->tag.desc_crc_len) + sizeof(struct part_map_1)); } #endif /* not called often; free incomming when not needed */ int udf_proc_log_vol(struct udf_pri_vol *primary, struct udf_log_vol **current, struct logvol_desc *incomming) { struct udf_log_vol *logical; struct udf_part_mapping *part_mapping, *data_part_mapping; union udf_pmap *pmap; uint32_t part_cnt, pmap_type, pmap_size; uint32_t data_part_num, raw_udf_phys_part_num; uint8_t *pmap_pos; logical = udf_search_logical_volume_in_primary(primary, incomming->logvol_id); if (!logical) { /* create a logical volume */ logical = calloc(1, sizeof(struct udf_log_vol)); if (!logical) { free(incomming); return ENOMEM; } /* link in */ SLIST_INSERT_HEAD(&primary->log_vols, logical, next_logvol); } else { /* ok ... we now need to check if this new descriptor is a newer version */ if (udf_rw32(incomming->seq_num) < udf_rw32(logical->log_vol->seq_num)) { /* its an older one; ignore */ free(incomming); return 0; } } /* update the logical volume descriptor and its mappings; first delete old partition mappings allocated before */ logical->primary = primary; if (current) *current = logical; part_mapping = SLIST_FIRST(&logical->part_mappings); while ((part_mapping = SLIST_FIRST(&logical->part_mappings))) { /* TODO cleanup old cruft ? (XXX while mounted? i dont think so!) */ /* free(part_mapping->sparing_table); free(part_mapping->vat_file_entry); free(part_mapping->vat); free(part_mapping->meta_file); free(part_mapping->meta_mirror_file); free(part_mapping->meta_bitmap_file); */ SLIST_REMOVE_HEAD(&logical->part_mappings, next_mapping); free(part_mapping); } SLIST_INIT(&logical->part_mappings); /* use the new logical volume and preprocess it */ if (logical->log_vol) free(logical->log_vol); logical->log_vol = incomming; logical->lb_size = udf_rw32(incomming->lb_size); logical->sector_size = primary->udf_session->disc->sector_size; /* build up the partion mappings */ logical->num_part_mappings = udf_rw32(incomming->n_pm); /* process partition mappings */ pmap_pos = &logical->log_vol->maps[0]; for (part_cnt = 0; part_cnt < logical->num_part_mappings; part_cnt++) { /* get a new part_mapping structure */ part_mapping = calloc(1, sizeof(struct udf_part_mapping)); assert(part_mapping); /* XXX check with partition mapping destructor etc XXX */ /* add to list */ SLIST_INSERT_HEAD(&logical->part_mappings, part_mapping, next_mapping); /* process */ pmap = (union udf_pmap *) pmap_pos; pmap_type = pmap->data[0]; pmap_size = pmap->data[1]; part_mapping->udf_virt_part_num = part_cnt; part_mapping->udf_pmap = pmap; switch (pmap_type) { case 1: part_mapping->udf_part_mapping_type = UDF_PART_MAPPING_PHYSICAL; part_mapping->vol_seq_num = udf_rw16(pmap->pm1.vol_seq_num); part_mapping->udf_phys_part_num = udf_rw16(pmap->pm1.part_num); break; case 2: part_mapping->vol_seq_num = udf_rw16(pmap->pm2.vol_seq_num); part_mapping->udf_phys_part_num = udf_rw16(pmap->pm2.part_num); if (strncmp((char *) pmap->pm2.part_id.id, "*UDF Virtual Partition", UDF_REGID_ID_SIZE) == 0) { part_mapping->udf_part_mapping_type = UDF_PART_MAPPING_VIRTUAL; break; } if (strncmp((char *) pmap->pm2.part_id.id, "*UDF Sparable Partition", UDF_REGID_ID_SIZE) == 0) { part_mapping->udf_part_mapping_type = UDF_PART_MAPPING_SPARABLE; break; } if (strncmp((char *) pmap->pm2.part_id.id, "*UDF Metadata Partition", UDF_REGID_ID_SIZE) == 0) { part_mapping->udf_part_mapping_type = UDF_PART_MAPPING_META; fprintf(stderr, "WARNING: `metadata' partition type encountered; its still in beta stage\n"); break; } printf("HELP ... found unsupported type 2 partition mapping id `%s`; marking broken\n", pmap->pm2.part_id.id); default: part_mapping->udf_part_mapping_type = UDF_PART_MAPPING_ERROR; } pmap_pos += pmap_size; /* variable length array :( */ } /* flag all partion mappings data and metadata writable */ SLIST_FOREACH(part_mapping, &logical->part_mappings, next_mapping) { part_mapping->data_writable = 1; part_mapping->metadata_writable = 1; } /* update writable flags depending on mapping type */ SLIST_FOREACH(part_mapping, &logical->part_mappings, next_mapping) { switch (part_mapping->udf_part_mapping_type) { case UDF_PART_MAPPING_ERROR : part_mapping->data_writable = 0; part_mapping->metadata_writable = 0; break; case UDF_PART_MAPPING_PHYSICAL : break; case UDF_PART_MAPPING_VIRTUAL : case UDF_PART_MAPPING_META : /* * These are special in that there is a special metadata partition where no data * is meant to be written on and vice versa */ /* find the associated data partition */ data_part_num = part_mapping->udf_phys_part_num; SLIST_FOREACH(data_part_mapping, &logical->part_mappings, next_mapping) { if (data_part_mapping->udf_phys_part_num == data_part_num) { if (data_part_mapping != part_mapping) { data_part_mapping->metadata_writable = 0; break; } } } part_mapping->data_writable = 0; break; case UDF_PART_MAPPING_SPARABLE : break; } } TAILQ_INIT(&logical->dirty_nodes); UDF_MUTEX_INIT(&logical->dirty_nodes_mutex); return 0; } /* not called often; free incomming when not needed */ int udf_proc_part(struct udf_pri_vol *primary, struct udf_partition **current, struct part_desc *incomming) { struct udf_partition *udf_partition; struct udf_volumeset *udf_volset; uint32_t new_part_num, sector_size; assert(primary); assert(primary->pri_vol); udf_volset = udf_search_volumeset(primary->pri_vol->volset_id); assert(udf_volset); new_part_num = udf_rw16(incomming->part_num); /* check if its a partition type we recognize */ if (strncmp((char *) incomming->contents.id, "+NSR0", 5) != 0) { fprintf(stderr, "Unrecognized partition content type %s encountered; ignoring\n", incomming->contents.id); free(incomming); return 0; } /* look if we allready got it */ SLIST_FOREACH(udf_partition, &udf_volset->parts, next_partition) { if (udf_rw16(udf_partition->partition->part_num) == new_part_num) break; } /* we have space... now check if this is a newer one than the one known */ if (udf_partition) { if (udf_rw32(incomming->seq_num) < udf_rw32(udf_partition->partition->seq_num)) { /* its an older version */ free(incomming); return 0; } } else { /* get us a new udf_partition */ udf_partition = calloc(1, sizeof(struct udf_partition)); if (!udf_partition) { free(incomming); return ENOMEM; } /* link it in */ SLIST_INSERT_HEAD(&udf_volset->parts, udf_partition, next_partition); } assert(udf_partition); /* copy this new partition descriptor in the list */ if (udf_partition->partition) free(udf_partition->partition); udf_partition->partition = incomming; udf_partition->udf_session = primary->udf_session; udf_volset->max_partnum = MAX(udf_volset->max_partnum, new_part_num+1); /* REVIEW why +1? */ /* initialise */ sector_size = primary->udf_session->disc->sector_size; UDF_MUTEX_INIT(&udf_partition->partition_space_mutex); TAILQ_INIT(&udf_partition->unalloc_space_queue); TAILQ_INIT(&udf_partition->freed_space_queue); udf_partition->part_offset = udf_rw32(incomming->start_loc) * sector_size; udf_partition->part_length = udf_rw32(incomming->part_len) * sector_size; /* udf_partition->access_type = udf_rw32(incomming->access_type); */ udf_partition->free_unalloc_space = udf_partition->free_freed_space = 0; if (current) *current = udf_partition; return 0; } /* not called often; free incomming when not needed */ int udf_proc_filesetdesc(struct udf_log_vol *udf_log_vol, struct fileset_desc *incomming) { struct udf_mountpoint *mp; if (udf_rw16(incomming->tag.id) != TAGID_FSD) { printf("IEEE! Encountered a non TAGID_FSD in this fileset descriptor sequence!!!\n"); free(incomming); return EFAULT; } /* lookup fileset descriptor in this logical volume; interestingly fileset_num is KEY! */ SLIST_FOREACH(mp, &udf_log_vol->mountpoints, logvol_next) { if (mp->fileset_desc->fileset_num == incomming->fileset_num) break; } if (!mp) { /* add a new mountpoint! */ mp = calloc(1, sizeof(struct udf_mountpoint)); if (!mp) { free(incomming); return ENOMEM; } mp->fileset_desc = incomming; /* insert into udf_log_vol and into mountables list */ SLIST_INSERT_HEAD(&udf_log_vol->mountpoints, mp, logvol_next); SLIST_INSERT_HEAD(&udf_mountables, mp, all_next); } else { /* should we update mountpoint? */ if (udf_rw32(incomming->fileset_desc_num) <= udf_rw32(mp->fileset_desc->fileset_desc_num)) { /* we allready got a newer one */ free(incomming); return 0; } fprintf(stderr, "UDF DEBUG: would be updating mountpoint... HELP!\n"); /* FIXME delete all inode hash entries */ /* XXX how to do that? inodes OK but associated vnodes? XXX */ #if 0 if (!SLIST_EMPTY(&mp->inodes)) { printf("UDF: asked to delete mountpoint with inodes in hashtable!\n"); printf("Can't cope with that... aborting\n"); exit(1); } #endif /* free old information (allready in lists though!) */ free(mp->fileset_desc); free(mp->mount_name); } mp->udf_log_vol = udf_log_vol; mp->fileset_desc = incomming; mp->mount_name = strdup(udf_get_compound_name(mp)); return 0; } int udf_retrieve_volume_space(struct udf_discinfo *disc, struct udf_session *udf_session, struct extent_ad *extent) { struct udf_pri_vol *udf_pri_vol; struct udf_log_vol *udf_log_vol; union dscrptr *dscr; uint32_t sector, length, dscr_len, num_sectors; uint32_t sector_size; int tag_id; int error; udf_pri_vol = NULL; sector = udf_rw32(extent->loc); length = udf_rw32(extent->len); sector_size = disc->sector_size; error = 0; /* XXX zero length area's possible? XXX */ while (length) { error = udf_read_session_descriptor(udf_session, sector, "volume descriptor", &dscr, &dscr_len); if (error) { if (dscr) free(dscr); break; } tag_id = udf_rw16(dscr->tag.id); num_sectors = (dscr_len + sector_size-1) / sector_size; /* proc volume descriptor starting at sector `volume_sector' */ UDF_VERBOSE_MAX(udf_dump_descriptor(dscr)); switch (tag_id) { case TAGID_PRI_VOL : error = udf_proc_pri_vol(udf_session, &udf_pri_vol, &dscr->pvd); break; case TAGID_PARTITION : error = udf_proc_part(udf_pri_vol, NULL, &dscr->pd); break; case TAGID_LOGVOL : error = udf_proc_log_vol(udf_pri_vol, &udf_log_vol, &dscr->lvd); if (!error) { /* first create empty integrity descriptor then modify it on input (for sanity) */ udf_derive_new_logvol_integrity(udf_log_vol); } break; case TAGID_TERM : free(dscr); return 0; /* terminator */ case TAGID_UNALLOC_SPACE : /* unallocated space descriptor */ /* Specifies space that is not claimed yet in partitions (!) */ UDF_VERBOSE(printf("\t\t`unallocated space descriptor' ignored\n")); break; case TAGID_IMP_VOL : /* implemenation use volume descriptor */ /* Specifies information relevant for the implementator */ UDF_VERBOSE_MAX(printf("\t\t`implementation use volume descriptor' ignored\n")); break; case TAGID_VOL : fprintf(stderr, "UDF : untested volume space extender encountered\n"); break; default : printf("XXX Unhandled volume sequence %d; freeing\n", tag_id); free(dscr); break; } length -= num_sectors * sector_size; sector += num_sectors; if (tag_id == TAGID_VOL) { sector = udf_rw32(dscr->vdp.next_vds_ex.loc); length = udf_rw32(dscr->vdp.next_vds_ex.len); free(dscr); } } return error; } int udf_get_filelength(union dscrptr *dscr, uint64_t *length) { int32_t fe_tag; fe_tag = udf_rw16(dscr->tag.id); if (fe_tag == TAGID_FENTRY) { *length = udf_rw64(dscr->fe.inf_len); return 0; } else if (fe_tag == TAGID_EXTFENTRY) { *length = udf_rw64(dscr->efe.inf_len); return 0; } return ENOENT; } /* can be passed either a file_entry or an extfil_entry trough fentry! */ int udf_check_for_vat(struct udf_log_vol *udf_log_vol, struct udf_part_mapping *part_mapping, uint32_t vat_lb, union dscrptr *dscr) { struct udf_part_mapping *s_part_mapping; struct udf_node *vat_udf_node; struct long_ad udf_icbptr; struct regid *regid; struct uio vat_uio; struct iovec vat_iovec; struct icb_tag *icbtag; struct timestamp *mtime; uint64_t vat_length, vat_entries; uint32_t *vat_pos, vpart_num; uint8_t *vat; int error, found; /* prepare a `uio' structure for reading in complete VAT file */ error = udf_get_filelength(dscr, &vat_length); if (error) return error; if (vat_length == 0) return EFAULT; vat = malloc(vat_length); if (!vat) return ENOMEM; /* move to uio_newuio(struct uio *uio) with fixed length uio_iovcnt? */ bzero(&vat_uio, sizeof(struct uio)); vat_uio.uio_rw = UIO_WRITE; /* WRITE into this space */ vat_uio.uio_iovcnt = 1; vat_uio.uio_iov = &vat_iovec; vat_uio.uio_offset = 0; /* begin at the start */ vat_uio.uio_resid = vat_length; /* fill in IO vector */ vat_uio.uio_iov->iov_base = vat; vat_uio.uio_iov->iov_len = vat_length; /* find our virtual partition number corresponding to our physical partition number; this sucks */ found = 0; SLIST_FOREACH(s_part_mapping, &udf_log_vol->part_mappings, next_mapping) { if (s_part_mapping->udf_phys_part_num == part_mapping->udf_phys_part_num) { if (s_part_mapping->udf_part_mapping_type == UDF_PART_MAPPING_PHYSICAL) { /* found it ! */ found = 1; vpart_num = s_part_mapping->udf_virt_part_num; } } } if (!found) { printf("Can't find accompanied physical volume\n"); return ENOENT; } /* prepare udf_icbptr file node for easy file reading */ udf_icbptr.loc.part_num = vpart_num; udf_icbptr.loc.lb_num = udf_rw32(vat_lb); udf_icbptr.len = udf_log_vol->lb_size; /* not used, but may not be zero */ /* * this udf_node creation and disposing may look a bit inefficient but * its beneficiary for normal file access. its only used once for * reading in the VAT. */ /* create the udf_vat_node; anonymous since it can't be in a mountpoint */ error = udf_readin_anon_udf_node(udf_log_vol, dscr, &udf_icbptr, "VAT", &vat_udf_node); if (!error) { DEBUG(printf("READ FILE PART UIO for VAT\n")); error = udf_read_file_part_uio(vat_udf_node, "VAT contents", 0, &vat_uio); DEBUG(printf("vat_uio rest %d\n", (uint32_t) vat_uio.uio_resid)); } /* XXX allow for SHORT VAT's ? XXX */ if (!error) { if (vat_uio.uio_resid) { fprintf(stderr, "Warning: VAT file can't be read in completely\n"); } part_mapping->vat_udf_node = vat_udf_node; part_mapping->vat = (struct udf_vat *) vat; part_mapping->vat_length = vat_length; /* extract next unique file ID from the VAT file entry's unique ID incremented by one */ udf_log_vol->next_unique_id = vat_udf_node->unique_id; /* ok? */ udf_increment_unique_id(udf_log_vol); /* fentry is confirmed to be either an file_entry or an extfile_entry here */ if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) { icbtag = &dscr->fe.icbtag; mtime = &dscr->fe.mtime; } else { icbtag = &dscr->efe.icbtag; mtime = &dscr->efe.mtime; } if (icbtag->file_type == UDF_ICB_FILETYPE_VAT) { /* we are in UDF 2.00+ userland */ part_mapping->vat_translation = ((uint8_t *) part_mapping->vat) + udf_rw16(part_mapping->vat->header_len); part_mapping->vat_entries = (vat_length - udf_rw16(part_mapping->vat->header_len))/4; udf_log_vol->num_files = udf_rw32(part_mapping->vat->num_files); udf_log_vol->num_directories = udf_rw32(part_mapping->vat->num_directories); udf_log_vol->min_udf_readver = udf_rw16(part_mapping->vat->min_udf_readver); udf_log_vol->min_udf_writever = udf_rw16(part_mapping->vat->min_udf_writever); udf_log_vol->max_udf_writever = udf_rw16(part_mapping->vat->max_udf_writever); /* TODO update logvol name */ } else { /* still in the old UDF 1.50 userland; update? its notoriously broken */ /* check the old UDF 1.50 VAT */ DEBUG(printf("CHECK UDF 1.50 VAT\n")); vat_pos = (uint32_t *) vat; vat_entries = (vat_length-36)/4; /* definition */ regid = (struct regid *) (vat_pos + vat_entries); error = (strncmp((char *) regid->id, "*UDF Virtual Alloc Tbl", 22) == 0) ? 0 : ENOENT; if (!error) { part_mapping->vat_entries = vat_entries; part_mapping->vat_translation = vat; part_mapping->vat = NULL; /* num files/dirs? */ } } if (!error) { UDF_VERBOSE(udf_dump_timestamp("\t\t\t\tmarked closed at ", mtime)); } } /* clean up uio structure */ if (error) { if (vat) free(vat); if (vat_udf_node) udf_dispose_udf_node(vat_udf_node); part_mapping->vat_udf_node = NULL; } return error; } int udf_retrieve_supporting_tables(struct udf_log_vol *udf_log_vol) { struct udf_partition *udf_partition; struct udf_part_mapping *part_mapping; struct udf_session *udf_session; struct long_ad udf_icbptr; union dscrptr *possible_vat_fe; union dscrptr *sparing_table_dscr; uint8_t *vat; uint32_t spar_loc; uint64_t first_vat_loc, vat_loc, last_vat_loc; uint32_t vat_part; uint32_t sector_size, lb_size; int part_num, spar_num; int session_num; int error; /* * if there are any virtual or sparable partition in this logical * volume, try to find their supporting tables so we can find the rest */ lb_size = udf_log_vol->lb_size; sector_size = udf_log_vol->sector_size; SLIST_FOREACH(part_mapping, &udf_log_vol->part_mappings, next_mapping) { part_num = part_mapping->udf_virt_part_num; udf_logvol_vpart_to_partition(udf_log_vol, part_num, NULL, &udf_partition); UDF_VERBOSE_TABLES(printf("\tFor partition mapping %d->%d\n", part_num, part_mapping->udf_phys_part_num)); switch (part_mapping->udf_part_mapping_type) { case UDF_PART_MAPPING_ERROR : /* nothing to be done for these */ break; case UDF_PART_MAPPING_PHYSICAL : /* nothing to be done for these; no supporting tables */ break; case UDF_PART_MAPPING_VIRTUAL : /* * we have to find a good VAT at the END of the session. Since VAT's are * only to be used on WORM's and need to written as last, the strategy is * to go for the predicted end of this session and walk UP */ udf_session = udf_log_vol->primary->udf_session; session_num = udf_session->session_num; UDF_VERBOSE_TABLES(printf("\t\tSearching for the VAT :\n")); if (udf_session->session_length == 0) { UDF_VERBOSE( printf("\t\tThis virtual partition is inaccessible since its its size is not known;\n"); printf("\t\tTry to insert the disc in a CD or DVD recordable device to access it.\n"); ); part_mapping->udf_part_mapping_type = UDF_PART_MAPPING_ERROR; continue; } vat = NULL; vat_part = 0; if (udf_session->disc->next_writable[session_num]) { last_vat_loc = udf_session->disc->next_writable[session_num]; } else { last_vat_loc = udf_session->disc->session_end[session_num]; } last_vat_loc += udf_session->disc->blockingnr; /* give some extra slack since sizes are not allways given up correctly */ first_vat_loc = last_vat_loc - 256; /* 8 blocks of 32 */ first_vat_loc = MAX(first_vat_loc, udf_session->disc->session_start[session_num]); /* try to find the fileid for the VAT; NOTE that we are reading backwards :( */ vat_loc = last_vat_loc; do { DEBUG( printf("Trying VAT at sector %d in session\n", (int) vat_loc) ); error = udf_read_session_descriptor(udf_session, vat_loc, "VAT file entry", &possible_vat_fe, NULL); if (!error) { error = udf_check_tag_presence(possible_vat_fe, TAGID_FENTRY); if (error) error = udf_check_tag_presence(possible_vat_fe, TAGID_EXTFENTRY); } if (!error) error = udf_check_tag_payload( possible_vat_fe); if (!error) error = udf_check_for_vat(udf_log_vol, part_mapping, vat_loc, possible_vat_fe); if (!error) { break; } else { if (possible_vat_fe) free(possible_vat_fe); vat_loc--; if (vat_loc < first_vat_loc) error = EIO; } } while (error != EIO); if (error) { printf("WARNING: was looking for a VAT but didnt find it; marking logical volume broken\n"); part_mapping->udf_part_mapping_type = UDF_PART_MAPPING_ERROR; udf_log_vol->logvol_state = UDF_INTEGRITY_OPEN; udf_log_vol->broken = 1; continue; } UDF_VERBOSE_TABLES(printf("\t\t\tfound VAT file-entry at device logical sector %d\n", (uint32_t) vat_loc)); UDF_VERBOSE_TABLES(printf("\t\t\tFound %d byte VAT descriptor+table\n", (uint32_t) part_mapping->vat_length)); UDF_VERBOSE_TABLES(udf_dump_descriptor(possible_vat_fe)); UDF_VERBOSE_MAX(udf_dump_vat_table(part_mapping)); if (part_mapping->vat_translation) { /* the presence of a correct VAT means the logical volume is in a closed state */ udf_log_vol->logvol_state = UDF_INTEGRITY_CLOSED; UDF_VERBOSE(printf("\t\t\t\tmarked closed due to presence of VAT\n")); /* XXX update `free' space by requesting the device's free space? XXX */ udf_log_vol->free_space = udf_partition->part_offset + udf_partition->part_length - vat_loc*sector_size; } if (!udf_session->disc->sequential) { UDF_VERBOSE(printf("\t\t\t\tenabling sequential media emulation\n")); udf_session->disc->sequential = 1; } break; case UDF_PART_MAPPING_SPARABLE : /* we have to find a good sparing table; address are in device logical blocks */ udf_session = udf_log_vol->primary->udf_session; for(spar_num = 0; spar_num < part_mapping->udf_pmap->pms.n_st; spar_num++) { spar_loc = udf_rw32(part_mapping->udf_pmap->pms.st_loc[spar_num]); /* fetch spar_loc's table ; on THIS session. */ error = udf_read_session_descriptor(udf_session, spar_loc, "Sparing table", &sparing_table_dscr, NULL); if (!error) error = udf_check_tag_presence(sparing_table_dscr, TAGID_SPARING_TABLE); if (!error) { UDF_VERBOSE_TABLES(printf("\t\tFound the sparing table\n")); part_mapping->sparing_table = &sparing_table_dscr->spt; break; } else { if (sparing_table_dscr) free(sparing_table_dscr); } } if (!part_mapping->sparing_table) { part_mapping->udf_part_mapping_type = UDF_PART_MAPPING_ERROR; } UDF_VERBOSE_TABLES(udf_dump_descriptor((union dscrptr *) part_mapping->sparing_table)); break; case UDF_PART_MAPPING_META : /* * set up common locator parts; the files are located inside the `part_num' partion where * this partition is a added layer on. */ udf_icbptr.loc.part_num = udf_rw16(part_mapping->udf_pmap->pmm.part_num); udf_icbptr.len = lb_size; /* defined as maximum size */ UDF_VERBOSE_TABLES(printf("Reading metadata partition filedescriptor\n")); udf_icbptr.loc.lb_num = udf_rw32(part_mapping->udf_pmap->pmm.meta_file_lbn); error = udf_readin_anon_udf_node(udf_log_vol, NULL, &udf_icbptr, "Metadata partition file descriptor", &part_mapping->meta_file); if (error == 0) UDF_VERBOSE_TABLES(udf_dump_descriptor((union dscrptr *) part_mapping->meta_file)); udf_icbptr.loc.lb_num = udf_rw32(part_mapping->udf_pmap->pmm.meta_mirror_file_lbn); if ((error == 0) && (udf_icbptr.loc.lb_num != -1)) { UDF_VERBOSE_TABLES(printf("Reading metadata partition mirror filedescriptor\n")); error = udf_readin_anon_udf_node(udf_log_vol, NULL, &udf_icbptr, "Metadata partition mirror file descriptor", &part_mapping->meta_mirror_file); if (error == 0) UDF_VERBOSE_TABLES(udf_dump_descriptor((union dscrptr *) part_mapping->meta_mirror_file)); error = 0; /* XXX ignoring error code for now */ } udf_icbptr.loc.lb_num = udf_rw32(part_mapping->udf_pmap->pmm.meta_bitmap_file_lbn); if ((error == 0) && (udf_icbptr.loc.lb_num != -1)) { UDF_VERBOSE_TABLES(printf("Reading metadata partition bitmap filedescriptor\n")); error = udf_readin_anon_udf_node(udf_log_vol, NULL, &udf_icbptr, "Metadata partition bitmap file descriptor", &part_mapping->meta_bitmap_file); if (error == 0) UDF_VERBOSE_TABLES(udf_dump_descriptor((union dscrptr *) part_mapping->meta_bitmap_file)); } /* if something is wrong, then mark it as a broken partition */ if (error) { /* TODO handle read-errors on the meta data and meta data mirror file descriptors. */ part_mapping->udf_part_mapping_type = UDF_PART_MAPPING_ERROR; } break; } } UDF_VERBOSE_TABLES(printf("\n")); if (udf_log_vol->broken) return EIO; return 0; } int udf_retrieve_space_tables(struct udf_log_vol *udf_log_vol) { struct udf_partition *udf_partition; struct udf_part_mapping *part_mapping; struct part_hdr_desc *part_hdr_desc; union dscrptr *dscrptr; uint32_t sector; uint32_t lb_size; uint64_t length; int part_num; int error; lb_size = udf_log_vol->lb_size; SLIST_FOREACH(part_mapping, &udf_log_vol->part_mappings, next_mapping) { part_num = part_mapping->udf_virt_part_num; UDF_VERBOSE_TABLES(printf("\tFor partition mapping %d->%d\n", part_num, part_mapping->udf_phys_part_num)); /* retrieve and process unallocated- and freed-space information for all used partitions of the logvol */ error = udf_logvol_vpart_to_partition(udf_log_vol, part_num, NULL, &udf_partition); assert(udf_partition); part_hdr_desc = &udf_partition->partition->pd_part_hdr; sector = udf_rw32(part_hdr_desc->unalloc_space_table.lb_num); length = udf_rw32(part_hdr_desc->unalloc_space_table.len); /* needed? */ if (length) { error = udf_read_logvol_descriptor(udf_log_vol, part_num, sector, "Unallocated space table", &dscrptr, NULL); UDF_VERBOSE_MAX(printf("\tUnalloced space table\n")); UDF_VERBOSE_MAX(udf_dump_descriptor(dscrptr)); /* udf_process_space_table(&udf_partition->unalloc_space, dscrptr); */ free(dscrptr); } sector = udf_rw32(part_hdr_desc->unalloc_space_bitmap.lb_num); length = udf_rw32(part_hdr_desc->unalloc_space_bitmap.len); if (length) { error = udf_read_logvol_descriptor(udf_log_vol, part_num, sector, "Unallocated space bitmap", &dscrptr, NULL); if (!error) { UDF_VERBOSE_MAX(printf("\tUnalloced space bitmap\n")); UDF_VERBOSE_MAX(udf_dump_descriptor(dscrptr)); udf_read_in_space_bitmap(&udf_partition->unalloc_space_queue, &dscrptr->sbd, lb_size, &udf_partition->free_unalloc_space); UDF_VERBOSE_TABLES(printf("\t\tPhysical partition's unallocated space : %"PRIu64"\n", udf_partition->free_unalloc_space)); udf_partition->unalloc_space_bitmap = &dscrptr->sbd; } else { printf("While reading in unallocated space bitmap : %s\n", strerror(error)); udf_partition->unalloc_space_bitmap = NULL; /* TODO mark read-only logvol */ } } sector = udf_rw32(part_hdr_desc->freed_space_table.lb_num); length = udf_rw32(part_hdr_desc->freed_space_table.len); if (length) { error = udf_read_logvol_descriptor(udf_log_vol, part_num, sector, "Freed space table", &dscrptr, NULL); UDF_VERBOSE_MAX(printf("\tFreed space table\n")); UDF_VERBOSE_MAX(udf_dump_descriptor(dscrptr)); /* udf_process_space_table(&udf_partition->freed_space, dscrptr); */ free(dscrptr); } sector = udf_rw32(part_hdr_desc->freed_space_bitmap.lb_num); length = udf_rw32(part_hdr_desc->freed_space_bitmap.len); if (length) { error = udf_read_logvol_descriptor(udf_log_vol, part_num, sector, "Freed space bitmap", &dscrptr, NULL); if (!error) { UDF_VERBOSE_MAX(printf("\tFreed space bitmap\n")); UDF_VERBOSE_MAX(udf_dump_descriptor(dscrptr)); udf_read_in_space_bitmap(&udf_partition->freed_space_queue, &dscrptr->sbd, lb_size, &udf_partition->free_freed_space); UDF_VERBOSE_TABLES(printf("\t\tPhysical partition's freed space : %"PRIu64"\n", udf_partition->free_unalloc_space)); udf_partition->freed_space_bitmap = &dscrptr->sbd; } else { printf("While reading in freed space bitmap : %s\n", strerror(error)); udf_partition->freed_space_bitmap = NULL; /* TODO mark read-only logvol */ } } } UDF_VERBOSE_TABLES(printf("\n")); return 0; } /* * Fileset descriptors are on a logical volume's partitions; since virtual * partitions are then also possible its OK to use the VAT for redefining the * fileset descriptors. */ int udf_retrieve_fileset_descriptor(struct udf_log_vol *udf_log_vol) { struct udf_mountpoint *mountable; struct long_ad *fsd_loc; struct fileset_desc *new_fsd; struct udf_node *vnode; uint32_t part_num, lb_num, length; int32_t error; error = 0; /* flag OK */ fsd_loc = &udf_log_vol->log_vol->_lvd_use.fsd_loc; part_num = udf_rw16(fsd_loc->loc.part_num); lb_num = udf_rw32(fsd_loc->loc.lb_num); length = udf_rw32(fsd_loc->len); while (length && !error) { UDF_VERBOSE_TABLES( printf("\tFileset descriptor extent at sector %d within partion %d for %d bytes\n", lb_num, part_num, length) ); /* only go for ONE fsb at a time */ error = udf_read_logvol_descriptor(udf_log_vol, part_num, lb_num, "Fileset descriptor", (union dscrptr **) &new_fsd, NULL); if (!error) error = udf_check_tag((union dscrptr *) new_fsd); /* TODO need a clearer handling unrecorded blocks here */ if (error || (!new_fsd) || (new_fsd && (udf_rw16(new_fsd->tag.id) == TAGID_TERM))) { /* end of sequence */ UDF_VERBOSE_TABLES( printf("\t\t(Terminator) "); if (!new_fsd || error) printf("; unrecorded"); else printf("; explicit"); printf("\n"); ); /* clear error to indicate end of sequence and free possible read in descriptor */ error = 0; if (new_fsd) free(new_fsd); break; } UDF_VERBOSE_MAX(udf_dump_descriptor((union dscrptr *) new_fsd)); udf_proc_filesetdesc(udf_log_vol, new_fsd); if (udf_rw32(new_fsd->next_ex.len) == 0) { /* next entry */ lb_num += 1; length -= udf_log_vol->lb_size; } else { /* follow the next extent */ fsd_loc = &new_fsd->next_ex; part_num = udf_rw16(fsd_loc->loc.part_num); lb_num = udf_rw32(fsd_loc->loc.lb_num); length = udf_rw32(fsd_loc->len); } } UDF_VERBOSE_TABLES(printf("\n")); if (error) return error; /* if no error occured, create rootdir udf_nodes */ SLIST_FOREACH(mountable, &udf_log_vol->mountpoints, logvol_next) { /* errors are OK */ udf_readin_anon_udf_node(udf_log_vol, NULL, &mountable->fileset_desc->rootdir_icb, "Rootdir", &mountable->rootdir_node); udf_readin_anon_udf_node(udf_log_vol, NULL, &mountable->fileset_desc->streamdir_icb, "Streamdir", &mountable->streamdir_node); /* keep names the same ? (duplicate code ahead ... ) */ if (mountable->rootdir_node) { vnode = mountable->rootdir_node; vnode->mountpoint = mountable; vnode->stat.st_uid = vnode->stat.st_gid = UINT_MAX; vnode->stat.st_mode = 0777 | S_IFDIR; udf_insert_node_in_hash(vnode); } if (mountable->streamdir_node) { vnode = mountable->streamdir_node; vnode->mountpoint = mountable; vnode->stat.st_uid = vnode->stat.st_gid = UINT_MAX; vnode->stat.st_mode = 0777 | S_IFDIR; udf_insert_node_in_hash(vnode); } } return 0; } int udf_check_writable_filesets(struct udf_log_vol *udf_log_vol, int mnt_flags) { struct udf_mountpoint *mp; int writable; writable = 1; if (mnt_flags & UDF_MNT_RDONLY) writable = 0; if (mnt_flags & UDF_MNT_FORCE) writable = 1; if (udf_log_vol->logvol_state == UDF_INTEGRITY_OPEN) { if (!(mnt_flags & UDF_MNT_FORCE)) { /* we explicitly DISABLE writing */ /* XXX do we even reach here? XXX */ if (udf_verbose) { printf("\t\t\t\tmounting READ-ONLY due to open integrity\n"); } else { printf("WARNING: mounting logical volume READ-ONLY due to open integrity\n"); } writable = 0; } else { printf("WARNING: ignoring open integrity\n"); } } /* follow all mountpoints of this logical volume and check if they are writable */ SLIST_FOREACH(mp, &udf_log_vol->mountpoints, logvol_next) { mp->writable = writable; } udf_log_vol->writable = writable; /* WAS: */ /* udf_log_vol->primary->udf_session->writable = mark; */ return 0; } /* * udf_eliminate_predescessor_volumesets() * * We are faced with a curious problem : we are to examine the partitions and * determine which are successors of eachother. This is propably most * relevant only on WORM media though. We could consider the following rules : * 1) `glue' according to strict UDF level 1 rules ? * 2) use heuristics info i.e. NERO, DirectCD and mkisofs quirks ? * 3) use overlapping partitions to detect relationships ? * * Using 1 would imply no multi-volume discs and thus glue everything but that * could easily be wrong. Selecting by volumeset names is not possible for * Nero f.e. just creates random volumeset names every session and uses no * volume version information and thus also violates the UDF rules. * * Using 2 would be tricky; we know a few programs but what if more are * developped? We then would be at loss. * * Using 3 would imply some calculation but is fail-safe in both supporting * multiple volumes on one disc (they seperate) and in supporting * multi-session WORM media for these will refer to eachother. Offcource NERO * could faul this by just extending the zero partion to the whole disc in its * ignorance and thus create false overlapping over other independent * sessions. This is to be investigated. I dont know how NERO will react on * this situation. * * Propably method 3 would be good to try : * * Follow the disc and check for all sessions in order to mark the ones with * overlapping partitions as `inactive' and keep the latest one active. * Sessions with the `local' quirk are seperate allmost by default; should * be change the offsets? would not be too difficult but possible. */ void udf_eliminate_predescessor_volumesets(struct udf_discinfo *disc) { struct udf_volumeset *anc_vol_set; struct udf_volumeset *sib_vol_set; struct pri_vol_desc *anc_pri_vol; struct pri_vol_desc *sib_pri_vol; struct udf_partition *anc_part; struct udf_partition *sib_part; int anc_partnum; int sib_partnum; uint32_t anc_start, anc_end; uint32_t sib_start, sib_end; uint32_t overlap_start, overlap_end; uint32_t anc_session; uint32_t sib_session; SLIST_FOREACH(anc_vol_set, &udf_volumeset_list, next_volumeset) { anc_pri_vol = STAILQ_FIRST(&anc_vol_set->primaries)->pri_vol; sib_vol_set = SLIST_NEXT(anc_vol_set, next_volumeset); while (sib_vol_set) { sib_pri_vol = STAILQ_FIRST(&sib_vol_set->primaries)->pri_vol; DEBUG( printf("checking volset %s with volset %s\n", anc_pri_vol->volset_id+1, sib_pri_vol->volset_id+1) ); /* compare these two volume sets but only process partitions on _this_ disc */ SLIST_FOREACH(anc_part, &anc_vol_set->parts, next_partition) { if (anc_part->udf_session->disc != disc) continue; anc_session = anc_part->udf_session->session_num; anc_start = 0; #if 0 if (disc->session_quirks[anc_session] & CD_SESS_QUIRK_SESSION_LOCAL) anc_start += disc->session_start[anc_session]; #endif anc_start += udf_rw32(anc_part->partition->start_loc); anc_end = anc_start + udf_rw32(anc_part->partition->part_len); SLIST_FOREACH(sib_part, &sib_vol_set->parts, next_partition) { if (sib_part->udf_session->disc != disc) continue; sib_session = sib_part->udf_session->session_num; #if 0 /* can `session local' volumes even be considered part/successor ? */ if (disc->session_quirks[sib_session] & CD_SESS_QUIRK_SESSION_LOCAL) continue; #endif sib_start = 0; #if 0 if (disc->session_quirks[sib_session] & CD_SESS_QUIRK_SESSION_LOCAL) sib_start += disc->session_start[sib_session]; #endif sib_start += udf_rw32(sib_part->partition->start_loc); sib_end = sib_start + udf_rw32(sib_part->partition->part_len); DEBUG( anc_partnum = udf_rw16(anc_part->partition->part_num); sib_partnum = udf_rw16(sib_part->partition->part_num); printf("\t\tchecking partition %d with partition %d ([%d-%d] x [%d-%d])\n", anc_partnum, sib_partnum, anc_start, anc_end, sib_start, sib_end) ); overlap_start = MAX(sib_start, anc_start); overlap_end = MIN(sib_end, sib_end); if (overlap_start < overlap_end) { DEBUG( printf("\t\t\tOVERLAP!\n") ); if (sib_session < anc_session) { /* the sibbling is older */ UDF_VERBOSE_TABLES( printf("\tVolume set "); udf_dump_id(NULL, 128, anc_pri_vol->vol_id, &anc_pri_vol->desc_charset); printf(" is a newer version of volume set "); udf_dump_id(NULL, 128, sib_pri_vol->vol_id, &sib_pri_vol->desc_charset); printf("\n"); ); sib_vol_set->obsolete = 1; break; } } /* overlap */ if (sib_vol_set->obsolete) break; } /* sibling partition */ if (sib_vol_set->obsolete) break; } /* ancestor partition */ sib_vol_set = SLIST_NEXT(sib_vol_set, next_volumeset); } /* sibling volume set */ } /* ancestor volume set */ } int udf_add_session_to_discinfo(struct udf_discinfo *disc, int session, struct anchor_vdp *avdp, int error) { struct udf_session *udf_session; udf_session = calloc(1, sizeof(struct udf_session)); assert(udf_session); if (!error) { memcpy(&udf_session->anchor, avdp, sizeof(struct anchor_vdp)); } udf_session->disc = disc; udf_session->session_num = session; udf_session->session_offset = 0; udf_session->session_length = disc->session_end[session] - disc->session_start[session]; disc->session_quirks[session] = 0; /* writable session administration */ udf_session->writable = 0; /* default off */ error = udf_init_session_caches(udf_session); if (!error) { /* detect quirks */ /* XXX session local disabled due to wrong heuristic XXX */ #if 0 if (disc->session_start[session] > 0) { if ((udf_session->anchor.main_vds_ex.loc < disc->session_start[session])) { disc->session_quirks[session] |= CD_SESS_QUIRK_SESSION_LOCAL; udf_session->session_offset = disc->session_start[session]; } } #endif } /* add to tail of session list */ STAILQ_INSERT_TAIL(&disc->sessions, udf_session, next_session); disc->num_udf_sessions++; /* record status of this volume */ disc->session_is_UDF[session] = error ? 0 : 1; return error; } int udf_get_anchors(struct udf_discinfo *disc) { uint8_t *sector; union dscrptr *dscr; uint32_t session_start, session_end; int session, error; /* Get all anchors */ STAILQ_INIT(&disc->sessions); sector = NULL; for (session = 0; session < disc->num_sessions; session++) { /* check for anchors ; no volume recognition data ? */ session_start = disc->session_start[session]; session_end = disc->session_end [session]-1; sector = calloc(1, disc->sector_size); if (!sector) return ENOMEM; dscr = (union dscrptr *) sector; error = udf_read_physical_sectors(disc, session_end, 1, "Anchor", sector); if (!error) error = udf_check_tag_presence(dscr, TAGID_ANCHOR); if (!error) UDF_VERBOSE_TABLES(printf("Accepting anchor at session end (%d)\n", session_end)); if (error) { error = udf_read_physical_sectors(disc, session_end - 256, 1, "Anchor", sector); if (!error) error = udf_check_tag_presence(dscr, TAGID_ANCHOR); if (!error) UDF_VERBOSE_TABLES(printf("Accepting anchor at session end - 256 (%d)\n", session_end - 256)); if (error) { error = udf_read_physical_sectors(disc, session_start + 256, 1, "Anchor", sector); if (!error) error = udf_check_tag_presence(dscr, TAGID_ANCHOR); if (!error) UDF_VERBOSE_TABLES(printf("Accepting anchor at session sector 256 (%d)\n", session_start + 256)); if (error) { /* unclosed CD recordable case due to track reservation for iso9660 filesystems */ error = udf_read_physical_sectors(disc, session_start + 512, 1, "Anchor", sector); if (!error) error = udf_check_tag_presence(dscr, TAGID_ANCHOR); if (!error) UDF_VERBOSE_TABLES(printf("Accepting anchor at session sector 512 (%d)\n", session_start + 512)); } } } if (!error) { udf_add_session_to_discinfo(disc, session, (struct anchor_vdp *) sector, error); } else { free(sector); } } return 0; } int udf_get_volumeset_space(struct udf_discinfo *disc) { struct udf_session *udf_session; int one_good_found; int error; /* Rip all volume spaces */ one_good_found = 0; UDF_VERBOSE(printf("\tretrieving volume space\n")); STAILQ_FOREACH(udf_session, &disc->sessions, next_session) { UDF_VERBOSE_MAX(printf("Session %d volumes : \n", udf_session->session_num)); error = udf_retrieve_volume_space(disc, udf_session, &udf_session->anchor.main_vds_ex); if (error) { printf("\nError retrieving session %d's volume space; prosessing reserve\n", udf_session->session_num); error = udf_retrieve_volume_space(disc, udf_session, &udf_session->anchor.reserve_vds_ex); } if (!error) one_good_found = 1; } return one_good_found ? 0 : ENOENT; } int udf_get_logical_volumes_supporting_tables(struct udf_discinfo *disc, int mnt_flags) { struct udf_volumeset *udf_volumeset; struct udf_pri_vol *udf_pri_vol; struct udf_log_vol *udf_log_vol; int logvolint_error; int one_good_found; int error; one_good_found = 0; SLIST_FOREACH(udf_volumeset, &udf_volumeset_list, next_volumeset) { if (!udf_volumeset->obsolete) { STAILQ_FOREACH(udf_pri_vol, &udf_volumeset->primaries, next_primary) { if (udf_pri_vol->udf_session->disc == disc) { SLIST_FOREACH(udf_log_vol, &udf_pri_vol->log_vols, next_logvol) { /* retrieving logical volume integrity sequence */ UDF_VERBOSE(udf_dump_volume_name("\t\tLogical volume ", udf_log_vol)); UDF_VERBOSE(printf("\t\t\tintegrity\n")); logvolint_error = udf_proc_logvol_integrity_sequence(udf_log_vol); /* load in supporting tables */ UDF_VERBOSE(printf("\t\t\tsupporting tables\n")); error = udf_retrieve_supporting_tables(udf_log_vol); /* if the state is still marked `open', its dirty and we mount read-only for safety */ if (logvolint_error) { printf("\t\t\t*** marked read-only due to logvol integrity error ***\n"); mnt_flags |= UDF_MNT_RDONLY; } if (udf_log_vol->logvol_state == UDF_INTEGRITY_OPEN) { printf("\t\t\t*** marked read-only due to open logical volume ***\n"); mnt_flags |= UDF_MNT_RDONLY; } /* get fileset descriptors */ UDF_VERBOSE(printf("\t\t\tfileset(s)\n")); if (!error) error = udf_retrieve_fileset_descriptor(udf_log_vol); /* check if the logical volume is writable */ UDF_VERBOSE(printf("\t\t\tchecking writable filesets\n")); if (!error) error = udf_check_writable_filesets(udf_log_vol, mnt_flags); /* load in free/used space tables for writable volsets */ UDF_VERBOSE(printf("\t\t\tused/freed space tables\n")); if (!error) error = udf_retrieve_space_tables(udf_log_vol); if (error) { udf_log_vol->broken = 1; } else { one_good_found = 1; } } /* logical */ } /* disc */ } /* primary */ } /* if */ } /* volumeset */ return one_good_found? 0 : ENOENT; } /****************************************************************************************** * * Disc sync * ******************************************************************************************/ void udf_sync_tables_callback(int reason, struct udf_wrcallback *wrcallback, int error, uint8_t *sectordata) { /* struct udf_node *udf_node = (struct udf_node *) wrcallback->structure; */ if (reason == UDF_WRCALLBACK_REASON_PENDING) { /* what to do? */ return; } if (reason == UDF_WRCALLBACK_REASON_ANULATE) { /* what to do? */ return; } assert(reason == UDF_WRCALLBACK_REASON_WRITTEN); if (error) { printf("UDF error: sync tables write errors in syncnode not fixed!\n"); return; } } /* TODO space tables are not coupled on a logical volume but on a partition/disc, so call them on that instead of logvol */ int udf_sync_space_tables(struct udf_log_vol *udf_log_vol) { struct udf_partition *udf_partition; struct udf_part_mapping *part_mapping; struct part_hdr_desc *part_hdr_desc; struct udf_wrcallback wr_callback; union dscrptr *dscrptr; uint64_t length; uint32_t sector; uint32_t lb_size, part_start, part_len; uint16_t dscr_ver; int part_num; int error; lb_size = udf_log_vol->lb_size; wr_callback.function = udf_sync_tables_callback; SLIST_FOREACH(part_mapping, &udf_log_vol->part_mappings, next_mapping) { part_num = part_mapping->udf_virt_part_num; UDF_VERBOSE_TABLES(printf("\tFor partition mapping %d->%d\n", part_num, part_mapping->udf_phys_part_num)); /* retrieve and process unallocated- and freed-space information for all used partitions of the logvol */ error = udf_logvol_vpart_to_partition(udf_log_vol, part_num, NULL, &udf_partition); assert(udf_partition); part_hdr_desc = &udf_partition->partition->pd_part_hdr; part_start = udf_rw32(udf_partition->partition->start_loc); part_len = udf_rw32(udf_partition->partition->part_len); dscr_ver = udf_rw16(udf_partition->partition->tag.descriptor_ver); sector = udf_rw32(part_hdr_desc->unalloc_space_table.lb_num); length = udf_rw32(part_hdr_desc->unalloc_space_table.len); /* needed? */ if (length) { printf("UDF: Can't write space tables yet\n"); #if 0 error = udf_read_logvol_descriptor(udf_log_vol, part_num, sector, "Unallocated space table", &dscrptr, NULL); UDF_VERBOSE_MAX(printf("\tUnalloced space table\n")); UDF_VERBOSE_MAX(udf_dump_descriptor(dscrptr)); //udf_process_space_table(&udf_partition->unalloc_space, dscrptr); free(dscrptr); #endif } sector = udf_rw32(part_hdr_desc->unalloc_space_bitmap.lb_num); length = udf_rw32(part_hdr_desc->unalloc_space_bitmap.len); /* printf("unalloc dscr at partition sector %d\n", sector); */ if (length) { /* read it in and modify */ dscrptr = (union dscrptr *) udf_partition->unalloc_space_bitmap; if (!dscrptr) { printf("Warning: creating empty unallocated space bitmap for partition's is gone\n"); error = udf_create_empty_space_bitmap(lb_size, dscr_ver, /* num_lbs */ part_len, (struct space_bitmap_desc **) &dscrptr); assert(!error); assert(udf_calc_tag_malloc_size(dscrptr, lb_size) <= length); udf_partition->unalloc_space_bitmap = &dscrptr->sbd; } udf_sync_space_bitmap(&udf_partition->unalloc_space_queue, &dscrptr->sbd, lb_size); UDF_VERBOSE_MAX(printf("\tWriteout unallocated space bitmap\n")); UDF_VERBOSE_MAX(udf_validate_tag_and_crc_sums((union dscrptr *) dscrptr); udf_dump_descriptor(dscrptr)); udf_write_partition_descriptor(udf_partition, sector, "Unallocated space bitmap", dscrptr, &wr_callback); /* SESSION descriptor!! */ } sector = udf_rw32(part_hdr_desc->freed_space_table.lb_num); length = udf_rw32(part_hdr_desc->freed_space_table.len); if (length) { printf("UDF: Can't write space tables yet\n"); #if 0 error = udf_read_logvol_descriptor(udf_log_vol, part_num, sector, "Freed space table", &dscrptr, NULL); UDF_VERBOSE_MAX(printf("\tFreed space table\n")); UDF_VERBOSE_MAX(udf_dump_descriptor(dscrptr)); //udf_process_space_table(&udf_partition->freed_space, dscrptr); free(dscrptr); #endif } sector = udf_rw32(part_hdr_desc->freed_space_bitmap.lb_num); length = udf_rw32(part_hdr_desc->freed_space_bitmap.len); /* printf("freed dscr at partition sector %d\n", sector); */ if (length) { /* read it in and modify */ dscrptr = (union dscrptr *) udf_partition->freed_space_bitmap; if (!dscrptr) { printf("Warning: creating empty freed space bitmap for partition's is gone\n"); error = udf_create_empty_space_bitmap(lb_size, dscr_ver, part_len, (struct space_bitmap_desc **) &dscrptr); assert(!error); assert(udf_calc_tag_malloc_size(dscrptr, lb_size) <= length); udf_partition->freed_space_bitmap = &dscrptr->sbd; } udf_sync_space_bitmap(&udf_partition->freed_space_queue, &dscrptr->sbd, lb_size); UDF_VERBOSE_MAX(printf("\tWriteout freed space bitmap\n")); UDF_VERBOSE_MAX(udf_validate_tag_and_crc_sums((union dscrptr *) dscrptr); udf_dump_descriptor(dscrptr)); udf_write_partition_descriptor(udf_partition, sector, "Freed space bitmap", dscrptr, &wr_callback); /* SESSION descriptor!! */ } } UDF_VERBOSE_TABLES(printf("\n")); return 0; } int udf_writeout_LVID(struct udf_log_vol *udf_log_vol, int type) { union dscrptr *dscr; struct logvol_int_desc *intdesc; struct udf_logvol_info *impl; struct udf_session *session; struct udf_partition *udf_partition; struct udf_part_mapping *part_mapping; struct desc_tag *terminator; struct udf_wrcallback wr_callback; uint32_t sector, lvid_sector, term_sector; uint32_t part_num, *free_space_pos, *size_pos, lb_size; uint32_t len, length, lvid_len, num_sectors; int error, dscr_ver, tagid; /* create a new `fresh' logvol integrity */ session = udf_log_vol->primary->udf_session; lb_size = udf_log_vol->lb_size; num_sectors = lb_size / session->disc->sector_size; intdesc = calloc(1, udf_log_vol->lb_size); if (!intdesc) return ENOMEM; sector = udf_rw32(udf_log_vol->log_vol->integrity_seq_loc.loc); length = udf_rw32(udf_log_vol->log_vol->integrity_seq_loc.len); if (!length) return ENOENT; /* search insertion place */ lvid_sector = 0; term_sector = 0; while (length) { error = udf_read_session_descriptor(udf_log_vol->primary->udf_session, sector, "Logical volume integrity descriptor (LVID)", &dscr, &lvid_len); /* getting a terminator tag or zero is an OK condition */ if (error) { tagid = 0; } else { tagid = udf_rw16(dscr->tag.id); } if ((tagid == TAGID_TERM) || (tagid == 0)) { lvid_sector = sector; if (length > lb_size) { /* space for a terminator */ term_sector = sector + num_sectors; } break; /* while */ } length -= lb_size; sector += num_sectors; if (udf_rw32(dscr->lvid.next_extent.len)) { sector = udf_rw32(dscr->lvid.next_extent.loc); length = udf_rw32(dscr->lvid.next_extent.len); } /* free consumed descriptor */ free(dscr); dscr = NULL; } if (dscr) free(dscr); if ((!lvid_sector) || (length == 0)) { sector = udf_rw32(udf_log_vol->log_vol->integrity_seq_loc.loc); length = udf_rw32(udf_log_vol->log_vol->integrity_seq_loc.len); lvid_sector = sector; if (length > lb_size) { /* space for a terminator */ term_sector = lvid_sector + num_sectors; } } assert(lvid_sector); /* build up integrety descriptor and write it out */ dscr_ver = udf_rw16(udf_log_vol->log_vol->tag.descriptor_ver); udf_init_desc_tag(&intdesc->tag, TAGID_LOGVOL_INTEGRITY, dscr_ver, udf_log_vol->integrity_serial); udf_set_timestamp_now(&intdesc->time); intdesc->integrity_type = udf_rw32(type); intdesc->lvint_next_unique_id = udf_rw64(udf_log_vol->next_unique_id); /* calculate and fill in free space */ intdesc->num_part = udf_rw32(udf_log_vol->num_part_mappings); free_space_pos = &intdesc->tables[0]; size_pos = &intdesc->tables[udf_log_vol->num_part_mappings]; SLIST_FOREACH(part_mapping, &udf_log_vol->part_mappings, next_mapping) { part_num = part_mapping->udf_virt_part_num; udf_logvol_vpart_to_partition(udf_log_vol, part_num, NULL, &udf_partition); assert(udf_partition); *size_pos++ = udf_partition->partition->part_len; *free_space_pos++ = udf_rw32(udf_partition->free_unalloc_space / udf_log_vol->lb_size); } /* fill in UDF implementation use parameters */ impl = (struct udf_logvol_info *) (&intdesc->tables[2*udf_log_vol->num_part_mappings]); udf_set_imp_id(&impl->impl_id); impl->num_files = udf_rw32(udf_log_vol->num_files); impl->num_directories = udf_rw32(udf_log_vol->num_directories); impl->min_udf_readver = udf_rw16(udf_log_vol->min_udf_readver); impl->min_udf_writever = udf_rw16(udf_log_vol->min_udf_writever); impl->max_udf_writever = udf_rw16(udf_log_vol->max_udf_writever); intdesc->l_iu = udf_rw32(sizeof(struct udf_logvol_info)); /* ECMA 3/10.10.7, UDF 2.2.6.4. */ len = sizeof(struct logvol_int_desc) - sizeof(uint32_t); /* length of logvol_int_desc without the extra table entry */ len += sizeof(uint32_t) * 2 * udf_log_vol->num_part_mappings; /* size and free space */ len += sizeof(struct udf_logvol_info); /* extra implementation use area */ len -= UDF_DESC_TAG_LENGTH; /* without header */ intdesc->tag.desc_crc_len = udf_rw16(len); udf_write_session_descriptor(session, lvid_sector, "Logvol integrity descriptor (LVID)", (union dscrptr *) intdesc, &wr_callback); if (session->disc->rewritable && term_sector) { /* only when there is space and its a rewritable media add a terminor */ error = udf_create_empty_terminator_descriptor(lb_size, dscr_ver, &terminator); if (!error) { udf_write_session_descriptor(session, term_sector, "Logvol integrity sequence descriptor sequence terminator", (union dscrptr *) terminator, &wr_callback); free(terminator); } } free(intdesc); return 0; } /* mark the logical volume `open'; for non-rewritables (CD-R/DVD+R/DVD-R) this is allmost a no-op */ int udf_open_logvol(struct udf_log_vol *udf_log_vol) { int error; if (!udf_log_vol->writable) { udf_dump_volume_name("\nLogical volume marked read only: ", udf_log_vol); return EROFS; } /* will return many times for each write */ if (udf_log_vol->logvol_state == UDF_INTEGRITY_OPEN) return 0; /* * Opening and closing logical volumes is derived from the state of * the primaries disc. */ udf_dump_volume_name("Opening logical volume", udf_log_vol); if (!udf_log_vol->primary->udf_session->disc->sequential) { error = udf_writeout_LVID(udf_log_vol, UDF_INTEGRITY_OPEN); assert(!error); /* sync caches to make sure all is written out */ udf_sync_caches(udf_log_vol); /* FIXME (callback) XXX ought to wait until we get the ALL-OK signal from the writeout-LVID action XXX */ } else { /* sequential recordable; any write just opens it; the descriptor is allready marked open */ } /* mark it open */ udf_log_vol->logvol_state = UDF_INTEGRITY_OPEN; return 0; } /* mark the logical volume in a `closed' state; close the integrity when possible for recordables writeout VAT */ int udf_close_logvol(struct udf_log_vol *udf_log_vol) { int error; if (udf_log_vol->logvol_state == UDF_INTEGRITY_CLOSED) { DEBUG(printf("close logvol: integrity allready closed\n")); return 0; } /* * Opening and closing logical volumes is derived from the state of * the primaries disc. */ udf_dump_volume_name("Closing logical volume", udf_log_vol); if (!udf_log_vol->primary->udf_session->disc->sequential) { error = udf_writeout_LVID(udf_log_vol, UDF_INTEGRITY_CLOSED); assert(!error); } else { /* XXX TODO XXX */ fprintf(stderr, "write out virtual sectors, compile VAT and write out VAT : not implemented\n"); return EIO; } /* sync caches to make sure all is written out */ udf_sync_caches(udf_log_vol); /* FIXME (callback) XXX ought to wait until we get the ALL-OK signal from the writeout-LVID action XXX */ /* mark it closed again */ udf_log_vol->logvol_state = UDF_INTEGRITY_CLOSED; return 0; } int udf_sync_logvol(struct udf_log_vol *udf_log_vol) { struct udf_node *udf_node; uint32_t num_dirty, count, prnt; int error; if (!udf_log_vol->writable) return 0; if (udf_log_vol->logvol_state == UDF_INTEGRITY_CLOSED) { DEBUG(printf("close logvol: its closed so no sync nessisary\n")); return 0; } UDF_VERBOSE(udf_dump_volume_name("\tsyncing ", udf_log_vol)); /* sync all nodes */ /* XXX syncing logvol sequential due to insertion sort in add node XXX */ num_dirty = 0; TAILQ_FOREACH(udf_node, &udf_log_vol->dirty_nodes, next_dirty) { num_dirty++; } /* * Purge all data out first, this will speed things up later (not * strickly nessissary since syncing a node will wait for all the data * to be written out first anyway */ count = num_dirty; prnt = 0; UDF_VERBOSE(printf("\t\tsyncing data\n")); TAILQ_FOREACH(udf_node, &udf_log_vol->dirty_nodes, next_dirty) { UDF_VERBOSE(printf("\r%8d", count); fflush(stdout)); udf_sync_udf_node(udf_node, "Sync Logvol"); count--; prnt = 1; } if (prnt) UDF_VERBOSE(printf("\r \r")); /* * Purge all nodes out... they ought to have no dirty buffers anymore * but they will write them out if deemed nessisary */ count = num_dirty; prnt = 0; UDF_VERBOSE(printf("\t\tsyncing nodes\n")); TAILQ_FOREACH(udf_node, &udf_log_vol->dirty_nodes, next_dirty) { UDF_VERBOSE(printf("\r%8d", count); fflush(stdout)); DEBUG(printf("N"); fflush(stdout)); udf_writeout_udf_node(udf_node, "Sync Logvol"); count--; prnt = 1; } if (prnt) UDF_VERBOSE(printf("\r \r")); /* shouldn't be nessisary */ udf_bufcache->flushall = 1; udf_purgethread_kick("Sync Logvol"); usleep(1); if (udf_bufcache->lru_len_dirty_metadata + udf_bufcache->lru_len_dirty_data) { printf("Warning: after syncing logvol dirty counts != 0 (%d, %d); please contact author.\n", udf_bufcache->lru_len_dirty_metadata, udf_bufcache->lru_len_dirty_data); } /* sync free and used space tables for writable volsets */ UDF_VERBOSE(printf("\t\tused/freed space tables\n")); error = udf_sync_space_tables(udf_log_vol); /* close logical volume */ udf_close_logvol(udf_log_vol); return error; } /* convenience routine */ int udf_sync_disc(struct udf_discinfo *disc) { struct udf_volumeset *udf_volumeset; struct udf_pri_vol *udf_pri_vol; struct udf_log_vol *udf_log_vol; SLIST_FOREACH(udf_volumeset, &udf_volumeset_list, next_volumeset) { if (!udf_volumeset->obsolete) { STAILQ_FOREACH(udf_pri_vol, &udf_volumeset->primaries, next_primary) { if (udf_pri_vol->udf_session->disc == disc) { SLIST_FOREACH(udf_log_vol, &udf_pri_vol->log_vols, next_logvol) { udf_sync_logvol(udf_log_vol); } /* logical */ } /* disc */ } /* primary */ } /* if */ } /* volumeset */ return 0; } /****************************************************************************************** * * UDF descriptor buildup and update functions * ******************************************************************************************/ static void udf_init_desc_tag(struct desc_tag *tag, uint16_t id, uint16_t dscr_ver, uint16_t serial_num) { bzero(tag, sizeof(struct desc_tag)); tag->id = udf_rw16(id); tag->descriptor_ver = udf_rw16(dscr_ver); tag->serial_num = udf_rw16(serial_num); /* the rest gets filled in when we write */ } static void udf_osta_charset(struct charspec *charspec) { bzero(charspec, sizeof(struct charspec)); charspec->type = 0; strcpy((char *) charspec->inf, "OSTA Compressed Unicode"); } static void udf_encode_osta_id(char *osta_id, uint16_t len, char *text) { uint16_t u16_name[1024]; uint8_t *pos; uint16_t *pos16; bzero(osta_id, len); if (!text) return; bzero(u16_name, sizeof(uint16_t) * 1023); /* convert ascii to 16 bits unicode */ pos = (uint8_t *) text; pos16 = u16_name; while (*pos) { *pos16 = *pos; pos++; pos16++; } *pos16 = 0; udf_CompressUnicode(len, 8, (unicode_t *) u16_name, (byte *) osta_id); /* Ecma 167/7.2.13 states that the length is recorded in the last byte */ osta_id[len-1] = strlen(text)+1; } static void udf_set_app_id(struct regid *regid) { bzero(regid, sizeof(struct regid)); regid->flags = 0; /* not dirty and not protected */ strcpy((char *) regid->id, APP_NAME); regid->id_suffix[0] = APP_VERSION_MAIN; regid->id_suffix[1] = APP_VERSION_SUB; } static void udf_set_imp_id(struct regid *regid) { bzero(regid, sizeof(struct regid)); regid->flags = 0; /* not dirty and not protected */ strcpy((char *) regid->id, IMPL_NAME); regid->id_suffix[0] = 4; /* unix */ regid->id_suffix[1] = 0; /* generic */ #if defined(__ANONYMOUSUDF__) #elif defined(__NetBSD__) regid->id_suffix[1] = 8; /* NetBSD */ #elif defined(__FreeBSD__) regid->id_suffix[1] = 7; /* FreeBSD */ #elif defined(LINUX) regid->id_suffix[1] = 5; /* Linux */ #endif } static void udf_set_entity_id(struct regid *regid, char *name, uint16_t UDF_version) { uint16_t *ver; bzero(regid, sizeof(struct regid)); regid->flags = 0; /* not dirty and not protected */ strcpy((char *) regid->id, name); ver = (uint16_t *) regid->id_suffix; *ver = udf_rw16(UDF_version); regid->id_suffix[2] = 4; /* unix */ regid->id_suffix[3] = 0; /* generic */ #if defined(__ANONYMOUSUDF__) #elif defined(__NetBSD__) regid->id_suffix[3] = 8; /* NetBSD */ #elif defined(__FreeBSD__) regid->id_suffix[3] = 7; /* FreeBSD */ #elif defined(LINUX) regid->id_suffix[3] = 5; /* Linux */ #endif } void udf_set_contents_id(struct regid *regid, char *content_id) { bzero(regid, sizeof(struct regid)); regid->flags = 0; strcpy((char *) regid->id, content_id); } /* XXX creators of empty descriptors could be externalised */ /* * result can be further processed using modify functions if demanded and then * processed trough udf_proc_pri_vol * [ int udf_proc_pri_vol(struct udf_session *udf_session, struct udf_pri_vol **current, struct pri_vol_desc *incomming); ] * */ int udf_create_empty_primary_volume_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint16_t serial, char *volset_id, char *privol_name, int vds_num, int max_vol_seq, struct pri_vol_desc **dscrptr) { struct pri_vol_desc *dscr; assert(dscrptr); *dscrptr = NULL; /* allocate and populate an empty primary volume descriptor */ dscr = malloc(sector_size); if (!dscr) return ENOMEM; bzero(dscr, sector_size); udf_init_desc_tag(&dscr->tag, TAGID_PRI_VOL, dscr_ver, 1); dscr->pvd_num = udf_rw32(serial); udf_encode_osta_id(dscr->vol_id, 32, privol_name); dscr->vds_num = udf_rw16(vds_num); dscr->max_vol_seq = udf_rw16(max_vol_seq); if (max_vol_seq > 1) { dscr->ichg_lvl = udf_rw16(3); /* signal its a single volume intended to be in a set */ dscr->max_ichg_lvl = udf_rw16(3); /* ,, */ dscr->flags = udf_rw16(1); /* signal relevance volumeset id */ } else { dscr->ichg_lvl = udf_rw16(2); /* signal its volume intended not to be in a set */ dscr->max_ichg_lvl = udf_rw16(2); /* ,, */ dscr->flags = udf_rw16(0); /* signal relevance volumeset id */ } dscr->charset_list = udf_rw32(1); /* only CS0 */ dscr->max_charset_list = udf_rw32(1); udf_encode_osta_id(dscr->volset_id, 128, volset_id); udf_osta_charset(&dscr->desc_charset); udf_osta_charset(&dscr->explanatory_charset); udf_set_app_id(&dscr->app_id); udf_set_imp_id(&dscr->imp_id); udf_set_timestamp_now(&dscr->time); dscr->tag.desc_crc_len = udf_rw16(sizeof(struct pri_vol_desc) - UDF_DESC_TAG_LENGTH); *dscrptr = dscr; return 0; } int udf_create_empty_partition_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint16_t serial, uint16_t part_num, uint32_t access_type, uint32_t start_loc, uint32_t part_len, uint32_t space_bitmap_size, uint32_t unalloc_space_bitmap, struct part_desc **dscrptr) { struct part_desc *dscr; struct part_hdr_desc *part_hdr; assert(dscrptr); *dscrptr = NULL; /* allocate and populate empty partition descriptor */ dscr = malloc(sector_size); /* only descriptor, no bitmap! */ if (!dscr) return ENOMEM; bzero(dscr, sector_size); udf_init_desc_tag(&dscr->tag, TAGID_PARTITION, dscr_ver, 1); dscr->seq_num = udf_rw32(serial); dscr->flags = udf_rw16(1); /* bit 0 : space is allocated */ dscr->part_num = udf_rw16(part_num); if (dscr_ver == 2) udf_set_contents_id(&dscr->contents, "+NSR02"); if (dscr_ver == 3) udf_set_contents_id(&dscr->contents, "+NSR03"); part_hdr = &dscr->pd_part_hdr; part_hdr->unalloc_space_bitmap.len = udf_rw32(space_bitmap_size); part_hdr->unalloc_space_bitmap.lb_num = udf_rw32(unalloc_space_bitmap); dscr->access_type = udf_rw32(access_type); dscr->start_loc = udf_rw32(start_loc); dscr->part_len = udf_rw32(part_len); udf_set_imp_id(&dscr->imp_id); /* why is this ignored? */ dscr->tag.desc_crc_len = udf_rw16(sizeof(struct part_desc) - UDF_DESC_TAG_LENGTH); *dscrptr = dscr; return 0; } int udf_create_empty_unallocated_space_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint16_t serial, struct unalloc_sp_desc **dscrptr) { struct unalloc_sp_desc *dscr; assert(dscrptr); *dscrptr = NULL; /* allocate and populate an empty unallocated space descriptor */ dscr = malloc(sector_size); if (!dscr) return ENOMEM; bzero(dscr, sector_size); udf_init_desc_tag(&dscr->tag, TAGID_UNALLOC_SPACE, dscr_ver, 1); dscr->seq_num = udf_rw32(serial); dscr->tag.desc_crc_len = udf_rw16(sizeof(struct unalloc_sp_desc) - sizeof(struct extent_ad) - UDF_DESC_TAG_LENGTH); *dscrptr = dscr; return 0; } int udf_create_empty_implementation_use_volume_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint16_t serial, char *logvol_name, struct impvol_desc **dscrptr) { struct impvol_desc *dscr; struct udf_lv_info *lv_info; assert(dscrptr); *dscrptr = NULL; /* allocate and populate an empty implementation use volume descriptor */ dscr = malloc(sector_size); if (!dscr) return ENOMEM; bzero(dscr, sector_size); udf_init_desc_tag(&dscr->tag, TAGID_IMP_VOL, dscr_ver, 1); dscr->seq_num = udf_rw32(serial); udf_set_entity_id(&dscr->impl_id, "*UDF LV Info", 0x102); /* just pick one; it'll be modifed later */ lv_info = &dscr->_impl_use.lv_info; udf_osta_charset(&lv_info->lvi_charset); udf_encode_osta_id(lv_info->logvol_id, 128, logvol_name); udf_encode_osta_id(lv_info->lvinfo1, 36, NULL); udf_encode_osta_id(lv_info->lvinfo2, 36, NULL); udf_encode_osta_id(lv_info->lvinfo3, 36, NULL); udf_set_imp_id(&lv_info->impl_id); dscr->tag.desc_crc_len = udf_rw16(sizeof(struct impvol_desc) - UDF_DESC_TAG_LENGTH); *dscrptr = dscr; return 0; } int udf_create_empty_logical_volume_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint16_t serial, char *logvol_name, uint32_t lb_size, uint32_t integrity_start, uint32_t integrity_length, struct logvol_desc **dscrptr) { struct logvol_desc *dscr; assert(dscrptr); *dscrptr = NULL; /* allocate and populate an empty logical volume descriptor */ dscr = malloc(sector_size); if (!dscr) return ENOMEM; bzero(dscr, sector_size); udf_init_desc_tag(&dscr->tag, TAGID_LOGVOL, dscr_ver, 1); dscr->seq_num = udf_rw32(serial); udf_osta_charset(&dscr->desc_charset); udf_encode_osta_id(dscr->logvol_id, 128, logvol_name); dscr->lb_size = udf_rw32(lb_size); udf_set_contents_id(&dscr->domain_id, "*OSTA UDF Compliant"); /* no fsd yet nor partition mapping */ udf_set_imp_id(&dscr->imp_id); dscr->integrity_seq_loc.loc = udf_rw32(integrity_start); dscr->integrity_seq_loc.len = udf_rw32(integrity_length * lb_size); dscr->tag.desc_crc_len = udf_rw16(sizeof(struct logvol_desc) - 1 - UDF_DESC_TAG_LENGTH); *dscrptr = dscr; return 0; } int udf_create_empty_space_bitmap(uint32_t sector_size, uint16_t dscr_ver, uint32_t num_lbs, struct space_bitmap_desc **dscrptr) { struct space_bitmap_desc *dscr; uint64_t bits; uint32_t bytes, space_bitmap_size; assert(dscrptr); *dscrptr = NULL; /* reserve space for unallocated space bitmap */ bits = num_lbs; bytes = (bits + 7)/8; space_bitmap_size = (bytes + sizeof(struct space_bitmap_desc)-1); /* round space bitmap size to sector size */ space_bitmap_size = ((space_bitmap_size + sector_size - 1) / sector_size) * sector_size; /* allocate and populate an empty space bitmap descriptor */ dscr = malloc(space_bitmap_size); if (!dscr) return ENOMEM; bzero(dscr, space_bitmap_size); udf_init_desc_tag(&dscr->tag, TAGID_SPACE_BITMAP, dscr_ver, 1); /* crc length 8 is recommended, UDF 2.3.1.2, 2.3.8.1, errata DCN-5108 for UDF 2.50 and lower. */ dscr->tag.desc_crc_len = udf_rw16(8); dscr->num_bits = udf_rw32(bits); dscr->num_bytes = udf_rw32(bytes); *dscrptr = dscr; return 0; } /* FIXME: no rootdir setting yet */ /* FIXME: fileset desc. is disc sector size or lb_size ? */ int udf_create_empty_fileset_desc(uint32_t sector_size, uint16_t dscr_ver, uint32_t fileset_num, char *logvol_name, char *fileset_name, struct fileset_desc **dscrptr) { struct fileset_desc *dscr; assert(dscrptr); *dscrptr = NULL; /* allocate and populate an empty logical volume descriptor */ dscr = malloc(sector_size); if (!dscr) return ENOMEM; bzero(dscr, sector_size); udf_init_desc_tag(&dscr->tag, TAGID_FSD, dscr_ver, 1); udf_set_timestamp_now(&dscr->time); dscr->ichg_lvl = udf_rw16(3); /* fixed? */ dscr->max_ichg_lvl = udf_rw16(3); /* fixed? */ dscr->charset_list = udf_rw32(1); /* only CS0 */ dscr->max_charset_list = udf_rw32(1); /* only CS0 */ dscr->fileset_num = udf_rw32(fileset_num); /* key for fileset */ dscr->fileset_desc_num = udf_rw32(0); /* fileset descriptor number as in copy # */ udf_osta_charset(&dscr->logvol_id_charset); udf_encode_osta_id(dscr->logvol_id, 128, logvol_name); udf_osta_charset(&dscr->fileset_charset); udf_encode_osta_id(dscr->fileset_id, 32, fileset_name); udf_encode_osta_id(dscr->copyright_file_id, 32, NULL); udf_encode_osta_id(dscr->abstract_file_id, 32, NULL); udf_set_contents_id(&dscr->domain_id, "*OSTA UDF Compliant"); dscr->tag.desc_crc_len = udf_rw16(sizeof(struct fileset_desc) - UDF_DESC_TAG_LENGTH); *dscrptr = dscr; return 0; } int udf_create_empty_anchor_volume_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint32_t main_vds_loc, uint32_t reserve_vds_loc, uint32_t length, struct anchor_vdp **vdp) { assert(vdp); assert(abs(main_vds_loc - reserve_vds_loc) >= length); *vdp = malloc(sector_size); if (!*vdp) return ENOMEM; bzero(*vdp, sector_size); udf_init_desc_tag(&(*vdp)->tag, TAGID_ANCHOR, dscr_ver, 1); (*vdp)->main_vds_ex.loc = udf_rw32(main_vds_loc); (*vdp)->main_vds_ex.len = udf_rw32(length * sector_size); (*vdp)->reserve_vds_ex.loc = udf_rw32(reserve_vds_loc); (*vdp)->reserve_vds_ex.len = udf_rw32(length * sector_size); (*vdp)->tag.desc_crc_len = udf_rw16(512-UDF_DESC_TAG_LENGTH); /* fixed size in Ecma */ return 0; } int udf_create_empty_terminator_descriptor(uint32_t sector_size, uint16_t dscr_ver, struct desc_tag **tag) { assert(tag); *tag = malloc(sector_size); if (!*tag) return ENOMEM; bzero(*tag, sector_size); udf_init_desc_tag(*tag, TAGID_TERM, dscr_ver, 1); (*tag)->desc_crc_len = udf_rw16(512-UDF_DESC_TAG_LENGTH); /* fixed size in Ecma */ return 0; } /****************************************************************************************** * * Basic `open' and `close' disc functions * ******************************************************************************************/ static void udf_process_session_range(struct udf_discinfo *disc, int *enabled, int low, int high) { int session; if (!disc) return; high = MIN(high, disc->num_sessions-1); session = low; for (session = low; session <= high; session++) { enabled[session] = 1; } } /* range is specified in -3,5,7 or 5-6,8- etc */ static int udf_process_session_range_string(struct udf_discinfo *disc, char *range) { struct udf_session *udf_session, *next_udf_session, *last_ok; char *pos, *nop; int low, high, len, session; int enabled[MAX_SESSIONS]; if (!range) return 0; DEBUG(printf("UDF range debugging string '%s'\n", range)); if (disc) { /* disable all */ for (session = 0; session < disc->num_sessions; session++) { enabled[session] = 0; } } /* parse string */ nop = strdup(range); pos = range; if (sscanf(pos, "-%u%n%s", &high, &len, nop) >= 1) { DEBUG(printf("UDF range match till %d\n", high)); udf_process_session_range(disc, enabled, 0, high); pos += len; } if (*pos && *pos == ',') pos++; while (*pos) { if (sscanf(pos, "%u%n%s", &low, &len, nop) >= 1) { pos += len; if (*pos == '-') { pos++; if (!*pos) { DEBUG(printf("UDF range match from %d\n", low)); udf_process_session_range(disc, enabled, low, INT_MAX); free(nop); return 0; } if (sscanf(pos, "%u%n%s", &high, &len, nop) >= 1) { pos += len; DEBUG(printf("UDF range match from %d to %d\n", low, high)); udf_process_session_range(disc, enabled, low, high); } } else { if (!*pos || (*pos == ',')) { DEBUG(printf("UDF range match %d\n", low)); udf_process_session_range(disc, enabled, low, low); } } if (*pos && (*pos != ',')) { fprintf(stderr, "UDF range matching : ',' expected at %s\n", pos); free(nop); return ENOENT; } pos++; } else { fprintf(stderr, "UDF range matching : number expected at %s\n", pos); free(nop); return ENOENT; } } free(nop); DEBUG(printf("UDF range matching : all ok till the end\n")); if (!disc) return 0; last_ok = NULL; udf_session = STAILQ_FIRST(&disc->sessions); while (udf_session) { next_udf_session = STAILQ_NEXT(udf_session, next_session); session = udf_session->session_num; if (!enabled[session]) { /* remove this session */ fprintf(stderr, "UDF: disabling UDF session %d on request\n", session); STAILQ_REMOVE(&disc->sessions, udf_session, udf_session, next_session); free(udf_session); disc->session_is_UDF[session] = 0; } udf_session = next_udf_session; } return 0; } int udf_check_session_range(char *range) { return udf_process_session_range_string(NULL, range); } int udf_mount_disc(char *devname, char *range, uint32_t sector_size, int mnt_flags, struct udf_discinfo **disc) { int error; /* XXX here? XXX */ udf_unix_init(); udf_start_unix_thread(); error = udf_open_disc(devname, disc); if ((!error) && sector_size) error = udf_discinfo_alter_perception(*disc, sector_size, 0); if (error) return error; error = udf_get_anchors(*disc); UDF_VERBOSE(udf_dump_disc_anchors(*disc)); if (range) { UDF_VERBOSE(printf("Selecting UDF sessions '%s' as specified\n", range)); udf_process_session_range_string(*disc, range); UDF_VERBOSE(udf_dump_disc_anchors(*disc)); } /* no UDF partitions so bail out */ if ((*disc)->num_udf_sessions == 0) return 0; UDF_VERBOSE(printf("Start mounting\n")); error = udf_get_volumeset_space(*disc); if (error) return error; UDF_VERBOSE(printf("\teliminating predescessors\n")); udf_eliminate_predescessor_volumesets(*disc); UDF_VERBOSE_TABLES(udf_dump_alive_sets()); UDF_VERBOSE(printf("\tretrieving logical volume dependencies\n")); error = udf_get_logical_volumes_supporting_tables(*disc, mnt_flags); UDF_VERBOSE_TABLES(udf_dump_alive_sets()); /* insert disc in the disc list */ SLIST_INSERT_HEAD(&udf_discs_list, *disc, next_disc); return error; } int udf_dismount_disc(struct udf_discinfo *disc) { UDF_VERBOSE(printf("Dismounting disc\n")); if (!disc->recordable) { /* easy way out: it was a read-only system */ UDF_VERBOSE(printf("\tdismounting readonly disc\n")); udf_stop_unix_thread(); udf_close_disc(disc); return 0; } /* Sync disc before closing it */ UDF_VERBOSE(printf("\tsyncing disc\n")); udf_sync_disc(disc); /* wait for the disc to idle */ UDF_VERBOSE(printf("\twait for syncing disc to idle\n")); while (!udf_discinfo_check_disc_ready(disc)) { sleep(1); } /* stop threads and finish writing to it */ udf_stop_unix_thread(); UDF_VERBOSE(printf("\tsignal disc its finished with writing\n")); udf_discinfo_finish_writing(disc); /* wait for the disc to idle again */ UDF_VERBOSE(printf("\twait for final disc idling\n")); while (!udf_discinfo_check_disc_ready(disc)) { sleep(1); } UDF_VERBOSE(printf("\tclose device\n")); udf_close_disc(disc); return 0; } /****************************************************************************************** * * Directory and other conversion UDF logic * Move to udf_unix.c / udf_vnops.c one day? * ******************************************************************************************/ static int udf_translate_icb_filetype_to_dirent_filetype(int udf_filetype) { int d_type; switch (udf_filetype) { case UDF_ICB_FILETYPE_DIRECTORY : d_type = DT_DIR; break; case UDF_ICB_FILETYPE_STREAMDIR : d_type = DT_DIR; break; case UDF_ICB_FILETYPE_FIFO : d_type = DT_FIFO; break; case UDF_ICB_FILETYPE_CHARDEVICE : d_type = DT_CHR; break; case UDF_ICB_FILETYPE_BLOCKDEVICE : d_type = DT_BLK; break; case UDF_ICB_FILETYPE_RANDOMACCESS : d_type = DT_REG; break; case UDF_ICB_FILETYPE_SYMLINK : d_type = DT_LNK; break; case UDF_ICB_FILETYPE_SOCKET : d_type = DT_SOCK; break; default : d_type = DT_UNKNOWN; break; } return d_type; } /* VOP_GETATTR */ /* allmost NOOP since we remember the stat in the inode */ int udf_getattr(struct udf_node *udf_node, struct stat *stat) { *stat = udf_node->stat; /* special: updatables */ stat->st_nlink = udf_node->link_cnt; stat->st_blocks = (stat->st_size + 512 -1)/512; /* blocks are hardcoded 512 bytes/sector in stat :-/ */ return 0; } /* VOP_SETATTR */ /* allmost NOOP since we remember the stat in the inode */ /* note VOP_SETATTR can selectively set attrs */ int udf_setattr(struct udf_node *udf_node, struct stat *stat) { if (!udf_node) return ENOENT; if (udf_open_logvol(udf_node->udf_log_vol)) return EROFS; /* FIXME please dont just copy everything ... XXX */ udf_node->stat = *stat; /* not attribute change time */ udf_set_timespec_now(&udf_node->stat.st_ctimespec); udf_node_mark_dirty(udf_node); return 0; } void udf_resync_fid_stream(uint8_t *buffer, uint32_t *pfid_pos, uint32_t max_fid_pos, int *phas_fids) { struct fileid_desc *fid; uint32_t fid_pos; int has_fids; assert(buffer); assert(pfid_pos); assert(phas_fids); has_fids = 0; fid_pos = *pfid_pos; while (!has_fids) { while (fid_pos <= max_fid_pos) { fid = (struct fileid_desc *) (buffer + fid_pos); if (udf_rw16(fid->tag.id) == TAGID_FID) break; /* fid's can only exist 4 bytes aligned */ fid_pos += 4; } if (fid_pos > max_fid_pos) { /* shouldn't happen ! to prevent chaos, do nothing */ /* XXX ought to give a warning? XXX */ has_fids = 0; break; } else { /* check if we found a valid FID */ fid = (struct fileid_desc *) (buffer + fid_pos); has_fids = (udf_check_tag((union dscrptr *) fid) == 0); if (has_fids) { assert(udf_rw16(fid->tag.id) == TAGID_FID); break; } } } *pfid_pos = fid_pos; *phas_fids = has_fids; } /* read one fid and process it into a dirent and advance to the next */ /* (*fid) has to be allocated a logical block in size, (*dirent) struct dirent length */ static int read_fid_stream(struct udf_node *dir_node, uint64_t *offset, struct fileid_desc *fid, struct dirent *dirent, struct udf_node **sub_node) { struct uio dir_uio; struct iovec dir_iovec; char *fid_name; uint32_t entry_length, lb_size; int enough, error; assert(fid); assert(dirent); assert(dir_node); assert(offset); assert(*offset != 1); lb_size = dir_node->udf_log_vol->lb_size; entry_length = 0; bzero(dirent, sizeof(struct dirent)); bzero(fid, lb_size); *sub_node = NULL; if (*offset >= dir_node->stat.st_size) return EINVAL; bzero(&dir_uio, sizeof(struct uio)); dir_uio.uio_rw = UIO_WRITE; /* write into this space */ dir_uio.uio_iovcnt = 1; dir_uio.uio_iov = &dir_iovec; dir_iovec.iov_base = fid; dir_iovec.iov_len = lb_size; dir_uio.uio_offset = *offset; dir_uio.uio_resid = MIN(dir_node->stat.st_size - (*offset), lb_size); error = udf_read_file_part_uio(dir_node, "file id" /* udf_node->dirent.d_name */, UDF_C_FIDS, &dir_uio); if (error) return error; /* * Check if we got a whole descriptor. * XXX Try to `resync' directory stream when something is very wrong. * */ enough = (dir_uio.uio_offset - (*offset) >= UDF_FID_SIZE); if (!enough) { /* short dir ... */ return EIO; } error = udf_check_tag((union dscrptr *) fid); if (!error) { entry_length = udf_calc_tag_malloc_size((union dscrptr *) fid, lb_size); enough = (dir_uio.uio_offset - (*offset) >= entry_length); } if (!enough) { /* short dir ... */ return EIO; } if (!error) error = udf_check_tag_payload((union dscrptr *) fid); if (error) { printf("BROKEN DIRECTORY ENTRY\n"); #if 0 // udf_dump_desc(&fid->tag); // udf_dump_fileid(fid); #endif /* RESYNC? */ /* TODO: use udf_resync_fid_stream */ return EIO; } /* we got a whole and valid descriptor */ /* create resulting dirent structure */ fid_name = (char *) fid->data + udf_rw16(fid->l_iu); dirent->d_fileno = udf_rw32(fid->icb.impl.im_used.unique_id); /* only 32 bits salvageable */ #if !defined(__DragonFly__) dirent->d_reclen = sizeof(struct dirent); #endif dirent->d_type = DT_UNKNOWN; udf_to_unix_name(dirent->d_name, fid_name, fid->l_fi, &dir_node->udf_log_vol->log_vol->desc_charset); if (fid->file_char & UDF_FILE_CHAR_DIR) dirent->d_type = DT_DIR; if (fid->file_char & UDF_FILE_CHAR_PAR) strcpy(dirent->d_name, ".."); /* advance */ *offset += entry_length; /* pre-fetch the node */ if ((fid->file_char & UDF_FILE_CHAR_DEL) == 0) { error = udf_readin_udf_node(dir_node, fid, sub_node); if (!error) { dirent->d_fileno = (*sub_node)->unique_id; dirent->d_type = udf_translate_icb_filetype_to_dirent_filetype((*sub_node)->udf_filetype); } } return error; } /* VOP_READDIR */ /* read in dirent's until the result_uio can't hold another */ int udf_readdir(struct udf_node *dir_node, struct uio *result_uio, int *eof_res /* int *cookies, int ncookies */) { struct udf_node *sub_node; struct fileid_desc *fid; struct dirent dirent; uint64_t pos; uint32_t lb_size; int eof, enough; int error; assert(eof_res); if (!dir_node) return EINVAL; if (!dir_node->udf_log_vol) return EINVAL; lb_size = dir_node->udf_log_vol->lb_size; /* check if we ever are going to even fit a single dirent back */ enough = (result_uio->uio_resid >= sizeof(struct dirent)); if (!enough) return EINVAL; fid = malloc(lb_size); if (!fid) return ENOMEM; /* check if we ought to insert dummy `.' node */ if (result_uio->uio_offset == 0) { bzero(&dirent, sizeof(struct dirent)); strcpy(dirent.d_name, "."); dirent.d_type = DT_DIR; uiomove(&dirent, sizeof(struct dirent), result_uio); /* mark with magic value (yeah it suxxs) that we have done the dummy */ result_uio->uio_offset = 1; } /* start directory reading; we remember our offset in the uio... is this sane? */ pos = result_uio->uio_offset; eof = (pos == dir_node->stat.st_size); enough = (result_uio->uio_resid >= sizeof(struct dirent)); while (enough && !eof) { /* readjust the offset when its flagged */ if (pos == 1) { result_uio->uio_offset = pos = 0; } /* read in FIDs */ error = read_fid_stream(dir_node, &pos, fid, &dirent, &sub_node); if (error) { printf("Error while reading directory file: %s\n", strerror(error)); free(fid); return error; } /* process this FID/dirent */ if ((fid->file_char & UDF_FILE_CHAR_DEL) == 0) { uiomove(&dirent, sizeof(struct dirent), result_uio); } /* pos is automatically advanced */ eof = (pos == dir_node->stat.st_size); enough = (result_uio->uio_resid >= sizeof(struct dirent)); } free(fid); result_uio->uio_offset = pos; if (eof) { if (eof_res) *eof_res = 1; return 0; } /* mark directory offset to call back with */ assert(!enough); if (eof_res) *eof_res = eof; return 0; } /* * XXX * speedup posibility; remember where in the dir_node you ended and start * from that entry on; this greatly helps lookup's of directory entries * XXX */ int udf_lookup(struct udf_node *dir_node, struct udf_node **res_node, char *name) { struct udf_node *sub_node; struct fileid_desc *fid; struct dirent dirent; uint64_t pos; uint32_t lb_size, to_do; int eof; int entry, error, found; if (!dir_node) return EINVAL; if (!res_node) return EINVAL; lb_size = dir_node->udf_log_vol->lb_size; /* lookup name in name cache and if not found lookup udf_node in directotory */ *res_node = NULL; DEBUG(printf("udf_lookup node = %"PRIu64" (%p), name = %s\n", dir_node->unique_id, dir_node, name)); /* only lookup in directories XXX DT_COMP also possible XXX */ if ((dir_node->stat.st_mode & S_IFDIR) == 0) return ENOTDIR; if (strcmp(name, ".") == 0) { *res_node = dir_node; return 0; } /* get space to read fid in */ fid = malloc(lb_size); if (!fid) return ENOMEM; /* start directory reading */ pos = 0; to_do = UINT_MAX; /* maximum entries to process */ entry = 0; /* number of entries processed */ eof = (pos == dir_node->stat.st_size); found = 0; while (!eof) { /* read in FIDs */ error = read_fid_stream(dir_node, &pos, fid, &dirent, &sub_node); if (error) { printf("Error while looking up node by name \"%s\" : %s\n", name, strerror(error)); free(fid); return error; } /* process this FID/dirent */ if ((fid->file_char & UDF_FILE_CHAR_DEL) == 0) { if ((!found) && (strcmp(dirent.d_name, name) == 0)) { *res_node = sub_node; found = 1; #if UDF_LOOKUP_READAHEAD > 0 to_do = UDF_LOOKUP_READAHEAD - (entry % UDF_LOOKUP_READAHEAD) + 1; #else to_do = 1; #endif } } entry++; to_do--; if (to_do == 0) break; /* pos is automatically advanced */ eof = (pos == dir_node->stat.st_size); } /* not found .... */ free(fid); if (!found) return ENOENT; return 0; } static int udf_count_direntries(struct udf_node *dir_node, int count_dotdot, uint32_t *dir_entries) { struct udf_node *sub_node; struct fileid_desc *fid; struct dirent dirent; uint64_t pos; uint32_t lb_size; int eof; int error; if (!dir_node) return EINVAL; lb_size = dir_node->udf_log_vol->lb_size; /* count all directory entries with optional the dotdot too */ /* only defined in directories XXX DT_COMP also possible XXX */ if ((dir_node->stat.st_mode & S_IFDIR) == 0) return ENOTDIR; /* get space to read fid in */ fid = malloc(lb_size); if (!fid) return ENOMEM; /* start directory reading */ *dir_entries = 0; pos = 0; eof = (pos == dir_node->stat.st_size); while (!eof) { /* read in FIDs */ error = read_fid_stream(dir_node, &pos, fid, &dirent, &sub_node); if (error) { printf("Error while counting directory entries : %s\n", strerror(error)); free(fid); return error; } /* process this FID/dirent */ if ((fid->file_char & UDF_FILE_CHAR_DEL) == 0) { if (fid->file_char & UDF_FILE_CHAR_PAR) { if (count_dotdot) *dir_entries = *dir_entries + 1; } else { *dir_entries = *dir_entries + 1; } } /* pos is automatically advanced */ eof = (pos == dir_node->stat.st_size); } /* end of directory */ free(fid); return 0; } static int udf_writeout_fid_info(struct udf_node *dir_node, struct fileid_desc *fid, uint64_t offset, uint16_t fid_len) { struct uio uio; struct iovec iovec; int flags; bzero(&uio, sizeof(struct uio)); uio.uio_rw = UIO_READ; /* read from this space */ uio.uio_iovcnt = 1; uio.uio_iov = &iovec; iovec.iov_base = fid; iovec.iov_len = fid_len; uio.uio_offset = offset; uio.uio_resid = fid_len; flags = UDF_C_FIDS; return udf_write_file_part_uio(dir_node, "file id.", flags, &uio); } /* search for a space to record the fid in, not checking if it is allready in it ! */ /* ALERT: not to be used to update a fid ... use writeout_fid_info for that */ static int udf_insert_fid_info(struct udf_node *dir_node, struct udf_node *udf_node, struct fileid_desc *i_fid, uint16_t fid_len) { struct udf_node *sub_node; struct fileid_desc *fid; struct dirent dirent; uint64_t fid_pos, pos; uint32_t lb_size, lb_rest; uint32_t chosen_length, chosen_offset, chosen_length_diff, padding; uint32_t entry_length, length_diff; int eof; int error; if (!dir_node) return EINVAL; lb_size = dir_node->udf_log_vol->lb_size; /* only defined in directories XXX DT_COMP also possible XXX */ if ((dir_node->stat.st_mode & S_IFDIR) == 0) return ENOTDIR; /* needs to be 4 bytes aligned to be legal! if not, something is seriously wrong so abort */ assert((fid_len & 3) == 0); /* get space to read fid in */ fid = malloc(lb_size); if (!fid) return ENOMEM; /* start directory reading */ pos = 0; chosen_length = 0; chosen_offset = 0; chosen_length_diff = UINT_MAX; eof = (pos == dir_node->stat.st_size); while (!eof) { /* read in FIDs */ fid_pos = pos; error = read_fid_stream(dir_node, &pos, fid, &dirent, &sub_node); if (error) { printf("Error while finding space to insert fid into : %s\n", strerror(error)); free(fid); return error; } /* process this FID/dirent */ entry_length = pos - fid_pos; if (fid->file_char & UDF_FILE_CHAR_DEL) { /* try this deleted fid entry */ if (entry_length >= fid_len) { length_diff = entry_length - fid_len; /* check if we would split up the FID tag (sanity) */ lb_rest = lb_size - (fid_pos % lb_size); if (lb_rest >= sizeof(struct desc_tag)) { if (length_diff < chosen_length_diff) { /* UDF 2.3.4.2+3 specifies rules for iu length */ if ((length_diff == 0) || (length_diff >= 32)) { chosen_length_diff = length_diff; chosen_offset = fid_pos; chosen_length = entry_length; } } } } } /* pos is automatically advanced */ eof = (pos == dir_node->stat.st_size); } /* end of directory */ if (chosen_length == 0) { /* append, but do not allow the next FID tag to be split */ chosen_offset = pos; chosen_length = fid_len; lb_rest = lb_size - ((chosen_offset + chosen_length) % lb_size); if (lb_rest < sizeof(struct desc_tag)) { /* add at least lb_rest to the length but make sure its padded to AT LEAST struct regid size */ chosen_length = MAX(chosen_length + sizeof(struct regid), chosen_length + lb_rest); } } if (chosen_length >= fid_len) { /* padding */ padding = chosen_length - fid_len; i_fid->tag.desc_crc_len = udf_rw16(chosen_length - UDF_DESC_TAG_LENGTH); i_fid->l_iu = udf_rw16(padding); if (padding) { assert(padding >= sizeof(struct regid)); memmove(i_fid->data + padding, i_fid->data, i_fid->l_fi); /* copy implementation regid in the padded space */ bzero(i_fid->data, padding-1); /* XXX -1? XXX */ udf_set_imp_id((struct regid *) i_fid->data); } /* sanity, just in case */ fid_len = chosen_length; } /* writeout modified piece */ udf_validate_tag_and_crc_sums((union dscrptr *) i_fid); error = udf_writeout_fid_info(dir_node, i_fid, chosen_offset, chosen_length); free(fid); return error; } /* create a file in the given directory with the given name and attributes using udf's file_char and udf'd filetype */ /* note * 1) that with `refering' node specified its effectively `link()' * 2) that with `refering' node specified, `filetype' is discarded as it ought to be the same as the `refering' one */ int udf_create_directory_entry(struct udf_node *dir_node, char *componentname, int filetype, int filechar, struct udf_node *refering, struct stat *stat, struct udf_node **new_node) { struct udf_allocentry *alloc_entry; struct udf_log_vol *udf_log_vol; struct udf_node *udf_node; struct charspec osta_charspec; struct fileid_desc *fid; uint32_t lb_num, lb_size; uint16_t vpart_num, descr_ver, len; int error; assert(dir_node); assert(componentname); assert(dir_node->udf_log_vol); udf_log_vol = dir_node->udf_log_vol; lb_size = udf_log_vol->lb_size; descr_ver = udf_rw16(udf_log_vol->log_vol->tag.descriptor_ver); *new_node = NULL; /* lookup if it allready exists (sanity... not nessisary in-kernel) */ error = udf_lookup(dir_node, new_node, componentname); if (!error || *new_node) { /* it existed! allready there */ return EEXIST; } if (!refering) { /* * Get ourselves an empty node and space to record file * descriptor in. */ error = udf_init_udf_node(dir_node->mountpoint, udf_log_vol, "New direntry", &udf_node); if (error) return error; udf_node->udf_filetype = filetype; udf_node->udf_filechar = filechar; udf_node->unique_id = udf_increment_unique_id(udf_log_vol); /* snif */ error = udf_allocate_udf_node_on_disc(udf_node); if (error) { assert(udf_node != dir_node); udf_dispose_udf_node(udf_node); return error; } udf_node->stat = *stat; /* note passed creation times; do sanitise them */ #ifndef NO_STAT_BIRTHTIME if (udf_insanetimespec(&stat->st_birthtimespec)) udf_set_timespec_now(&udf_node->stat.st_birthtimespec); #endif if (udf_insanetimespec(&stat->st_ctimespec)) udf_set_timespec_now(&udf_node->stat.st_ctimespec); if (udf_insanetimespec(&stat->st_atimespec)) udf_set_timespec_now(&udf_node->stat.st_atimespec); if (udf_insanetimespec(&stat->st_mtimespec)) udf_set_timespec_now(&udf_node->stat.st_mtimespec); } else { /* refering->ignore passed stat info */ udf_node = refering; filetype = udf_node->udf_filetype; /* linking changes metadata modification */ udf_set_timespec_now(&udf_node->stat.st_ctimespec); } alloc_entry = TAILQ_FIRST(&udf_node->dscr_allocs); vpart_num = alloc_entry->vpart_num; lb_num = alloc_entry->lb_num; /* build up new directory entry */ fid = calloc(1, lb_size); if (!fid) { if (!refering) udf_dispose_udf_node(udf_node); return ENOMEM; } udf_osta_charset(&osta_charspec); udf_init_desc_tag(&fid->tag, TAGID_FID, descr_ver, 1); /* tag serial number */ if (filechar & UDF_FILE_CHAR_PAR) { /* parent or `..' is not allowed to have a name length ... wierd but ok */ fid->l_fi = 0; } else { unix_to_udf_name((char *) fid->data, componentname, &fid->l_fi, &osta_charspec); } fid->file_version_num = udf_rw16(1); /* new file/dir; version starts at 1 */ fid->file_char = filechar; /* what is it */ fid->l_iu = udf_rw32(0); /* no impl. use */ fid->icb.len = udf_rw32(lb_size); /* fill in location */ fid->icb.loc.part_num = udf_rw16(vpart_num); fid->icb.loc.lb_num = udf_rw32(lb_num); /* fill in lower 32 bits of unique ID (UDF 3/3.2.2.1) in the impl use part of the FID's long_ad */ fid->icb.impl.im_used.unique_id = udf_rw32(((udf_node->unique_id << 32) >> 32)); /* calculate minimum size needed for directory entry */ len = UDF_FID_SIZE + fid->l_fi; len = (len + 3) & ~3; fid->tag.desc_crc_len = udf_rw16(len - UDF_DESC_TAG_LENGTH); error = udf_insert_fid_info(dir_node, udf_node, fid, len); if (error) { fprintf(stderr, "UDF: fid insertion failed : %s\n", strerror(error)); if (!refering) udf_dispose_udf_node(udf_node); return error; } free(fid); /* Ahum... easily forgotten here */ if (udf_node) { /* only insert file in hashlist if its not an explicit reference */ if (!refering) { udf_insert_node_in_hash(udf_node); } else { refering->link_cnt++; udf_node_mark_dirty(refering); } udf_node_mark_dirty(udf_node); } *new_node = udf_node; return error; } /* * Rename file from old_name to new_name. `present' is the file to be replaced * if found present allready. Care should be taken that the directory tree is * kept intact. To prevent this no path should be possible from the new parent * to the node to be renamed if it considers a directory and the new_parent is * not equal to the old parent. */ /* * VOP_RENAME(struct vnode *fdvp, struct vnode *vp, * struct componentname *fcnp, struct componentname *tdvp, * struct vnode *tvp, struct componentname *tcnp * ); */ int udf_rename(struct udf_node *old_parent, struct udf_node *rename_me, char *old_name, struct udf_node *new_parent, struct udf_node *present, char *new_name) { struct udf_node *new_node; int error; /* sanity */ if (!old_parent) return ENOENT; if (!new_parent) return ENOENT; if (!rename_me) return ENOENT; if (!(old_parent->stat.st_mode & S_IFDIR)) return ENOTDIR; if (!(new_parent->stat.st_mode & S_IFDIR)) return ENOTDIR; if (udf_open_logvol(old_parent->udf_log_vol)) return EROFS; if (udf_open_logvol(new_parent->udf_log_vol)) return EROFS; if ((present && (present->stat.st_mode & S_IFDIR)) || (old_parent != new_parent)) { /* cross directory moves */ fprintf(stderr, "Cross directory renaming is not implemented yet.\n"); return ENOTSUP; } /* if it was present, delete old contents; reference counting is done */ if (present) { /* TODO what about non dir, non file entries? */ if (present->stat.st_mode & S_IFDIR) { error = udf_remove_directory(new_parent, present, new_name); } else { error = udf_remove_file(new_parent, present, new_name); } if (error) return error; } /* insert new_name HARD-linked to the `rename_me' node */ error = udf_create_directory_entry(new_parent, new_name, rename_me->udf_filetype, rename_me->udf_filechar, rename_me, NULL, &new_node); if (error) return error; /* extra sanity */ if (!new_node) return ENOENT; /* 3) remove old link and mark directories dirty */ error = udf_remove_file_entry(old_parent, rename_me, old_name); udf_node_mark_dirty(old_parent); udf_node_mark_dirty(new_parent); return error; } /* VOP_CREATE */ int udf_create_file(struct udf_node *dir_node, char *componentname, struct stat *stat, struct udf_node **new_node) { struct udf_log_vol *udf_log_vol; struct udf_node *udf_node; uint32_t lb_size; int error; if (!dir_node) return EINVAL; udf_log_vol = dir_node->udf_log_vol; if (!udf_log_vol) return EINVAL; lb_size = udf_log_vol->lb_size; if (!udf_confirm_freespace(udf_log_vol, UDF_C_NODE, lb_size)) return ENOSPC; if (udf_open_logvol(dir_node->udf_log_vol)) return EROFS; error = udf_create_directory_entry(dir_node, componentname, UDF_ICB_FILETYPE_RANDOMACCESS, 0, NULL, stat, new_node); if ((!error) && (*new_node)) { udf_node = *new_node; /* update sizes */ udf_node->stat.st_size = 0; udf_node->stat.st_blksize = dir_node->udf_log_vol->lb_size; udf_node->stat.st_blocks = 0; /* not 1? */ udf_node->udf_log_vol->num_files++; udf_node_mark_dirty(udf_node); } return error; } /* VOP_MKDIR */ int udf_create_directory(struct udf_node *dir_node, char *componentname, struct stat *stat, struct udf_node **new_node) { struct udf_log_vol *udf_log_vol; struct udf_node *udf_node, *dummy_node; uint32_t lb_size; int error; if (!dir_node) return EINVAL; udf_log_vol = dir_node->udf_log_vol; if (!udf_log_vol) return EINVAL; lb_size = udf_log_vol->lb_size; if (!udf_confirm_freespace(udf_log_vol, UDF_C_NODE, 2*lb_size)) return ENOSPC; if (udf_open_logvol(dir_node->udf_log_vol)) return EROFS; stat->st_mode |= S_IFDIR; error = udf_create_directory_entry(dir_node, componentname, UDF_ICB_FILETYPE_DIRECTORY, UDF_FILE_CHAR_DIR, NULL, stat, new_node); if ((!error) && (*new_node)) { udf_node = *new_node; /* update sizes */ udf_node->stat.st_size = 0; udf_node->stat.st_blksize = dir_node->udf_log_vol->lb_size; udf_node->stat.st_blocks = 0; /* not 1? */ udf_node->udf_log_vol->num_directories++; udf_node_mark_dirty(udf_node); /* create `..' directory entry */ error = udf_create_directory_entry(udf_node, "..", UDF_ICB_FILETYPE_DIRECTORY, UDF_FILE_CHAR_DIR | UDF_FILE_CHAR_PAR, dir_node, stat, &dummy_node); if (error) { /* use of _prim for dir counting might not go well due to aborted creation */ error = udf_remove_directory_prim(dir_node, udf_node, componentname); } } return error; } /* really deletes all space referenced to this udf node including descriptor spaces and removes it from the administration */ int udf_unlink_node(struct udf_node *udf_node) { struct udf_allocentry *alloc_entry; uint32_t lbnum, len; uint16_t vpart; int error, flags; /* just in case its called from outside */ if (udf_open_logvol(udf_node->udf_log_vol)) return EROFS; /* unlinking changes metadata modification */ udf_set_timespec_now(&udf_node->stat.st_ctimespec); udf_node->link_cnt--; udf_node_mark_dirty(udf_node); if (udf_node->link_cnt > 0) return 0; /* trunc node */ udf_truncate_node(udf_node, (uint64_t) 0); /* get rid of file contents */ /* free descriptors from dscr_allocs queue */ TAILQ_FOREACH(alloc_entry, &udf_node->dscr_allocs, next_alloc) { vpart = alloc_entry->vpart_num; lbnum = alloc_entry->lb_num; flags = alloc_entry->flags; len = alloc_entry->len; error = udf_release_lbs(udf_node->udf_log_vol, vpart, lbnum, len); /* what if an error occures? */ assert(error == 0); } /* delete from administration */ udf_dispose_udf_node(udf_node); return 0; } /* NOTE: Dont use the EXTENT erased part; its for non sequential WORM only */ /* UDF 2.3.10.1, ECMA 4/48.14.1.1 */ /* fid->icb.impl.im_used.flags = udf_rw16(UDF_ADIMP_FLAGS_EXTENT_ERASED); */ static int udf_remove_file_entry(struct udf_node *dir_node, struct udf_node *udf_node, char *componentname) { struct udf_node *sub_node; struct fileid_desc *fid; struct dirent dirent; uint64_t fid_pos, pos; uint32_t lb_size; int eof; int error; assert(dir_node); assert(udf_node); assert(udf_node->udf_log_vol); lb_size = udf_node->udf_log_vol->lb_size; if (strncmp(componentname, "..", 3) == 0) { printf("Asked to remove `..' parent directory identifier; not allowed!\n"); return ENOENT; } if (strncmp(componentname, ".", 2) == 0) { printf("Asked to remove `.' current directory identifier; not allowed!\n"); return ENOENT; } /* only lookup in directories XXX DT_COMP also possible XXX */ if ((dir_node->stat.st_mode & S_IFDIR) == 0) return ENOTDIR; if (strcmp(componentname, ".") == 0) { return ENOENT; } /* get space to read fid in */ fid = malloc(lb_size); if (!fid) return ENOMEM; /* start directory reading */ pos = 0; eof = (pos == dir_node->stat.st_size); while (!eof) { /* read in FIDs */ fid_pos = pos; error = read_fid_stream(dir_node, &pos, fid, &dirent, &sub_node); if (error) { printf("Error while looking up node to be deleted \"%s\" : %s\n", componentname, strerror(error)); free(fid); return error; } /* process this FID/dirent */ if ((fid->file_char & UDF_FILE_CHAR_DEL) == 0) { if (strcmp(dirent.d_name, componentname) == 0) { /* mark node as deleted */ fid->file_char |= UDF_FILE_CHAR_DEL; bzero(&fid->icb, sizeof(struct long_ad)); udf_validate_tag_and_crc_sums((union dscrptr *) fid); udf_writeout_fid_info(dir_node, fid, fid_pos, pos-fid_pos); /* delete node and its administration */ udf_unlink_node(udf_node); free(fid); return 0; } } /* pos is automatically advanced */ eof = (pos == dir_node->stat.st_size); } /* not found .... */ free(fid); return ENOENT; } /* VOP_REMOVE */ int udf_remove_file(struct udf_node *dir_node, struct udf_node *udf_node, char *componentname) { int error; if (udf_open_logvol(dir_node->udf_log_vol)) return EROFS; if (udf_node->stat.st_mode & S_IFDIR) { /* only remove files with this call */ return EISDIR; } error = udf_remove_file_entry(dir_node, udf_node, componentname); if (!error) { dir_node->udf_log_vol->num_files--; } /* else? */ return error; } static int udf_remove_directory_prim(struct udf_node *dir_node, struct udf_node *udf_node, char *componentname) { int error; if (udf_open_logvol(dir_node->udf_log_vol)) return EROFS; /* remove the entry */ error = udf_remove_file_entry(dir_node, udf_node, componentname); if (!error) { dir_node->link_cnt--; udf_node_mark_dirty(dir_node); dir_node->udf_log_vol->num_directories--; } else { /* whoah! something went wrong, mark the .. as present again */ printf("UDF warning: filesystem might by in compromised state\n"); assert(udf_node); udf_node->link_cnt++; } return error; } /* VOP_RMDIR */ int udf_remove_directory(struct udf_node *dir_node, struct udf_node *udf_node, char *componentname) { uint32_t num_nodes; int error; if (!(udf_node->stat.st_mode & S_IFDIR)) { /* only remove directories with this call */ return ENOTDIR; } error = udf_count_direntries(udf_node, 0, &num_nodes); if (error) return error; if (num_nodes != 0) return ENOTEMPTY; error = udf_remove_directory_prim(dir_node, udf_node, componentname); return error; } /* end of udf.c */