- Main Page
- Namespaces
- Classes
- Files
- Directories

- File List
- File Members

# udf.cpp

    00001 /*************************************************************************
    00002   vStrip by [maven] (maven@maven.de)
    00003         udf.c:  routines for udf-parsing (because windows just doesn't cut it),
    00004                                         refs: udf102.pdf, udf200.pdf, ecma 167
    00005                                         (tabsize 2)
    00006 *************************************************************************/
    00007
    00008 #include "stdafx.h"
    00009 #include "udf.h"
    00010
    00011 static bool aspi_GetSectorInfo(const HANDLE hDrive, DWORD* sec_size, DWORD* max_sec)
    00012 {
    00013         LARGE_INTEGER size;
    00014         size.LowPart = GetFileSize(hDrive, (DWORD*)&size.HighPart);
    00015
    00016         *sec_size = 2048;
    00017         *max_sec = size.QuadPart / *sec_size;
    00018
    00019         return true;
    00020 }
    00021
    00022 static bool aspi_ReadSectors(const HANDLE hDrive, int lba, int nSectors, DWORD sec_size, BYTE* sector)
    00023 {
    00024         DWORD nbr = 0;
    00025         return lba*sec_size == SetFilePointer(hDrive, lba*sec_size, NULL, FILE_BEGIN)
    00026                 && ReadFile(hDrive, sector, nSectors*sec_size, &nbr, NULL);
    00027 }
    00028
    00029 static bool udf_GetLBA(const tp_udf_FileEntry fe, const DWORD sec_size, DWORD *start, DWORD *end)
    00030 {
    00031         if (fe->LengthofAllocationDescriptors == 0)
    00032                 return false;
    00033         switch (fe->ICBTag.Flags & udf_icbf_Mask)
    00034         {
    00035                 case udf_icbf_ShortAd:
    00036                 {
    00037                         tp_udf_short_ad ad = (tp_udf_short_ad)(fe->ExtendedAttributes + fe->LengthofExtendedAttributes);
    00038
    00039                         *start = ad->Location;
    00040                         *end = *start + ((ad->Length & udf_LengthMask) - 1) / sec_size;
    00041                         return true;
    00042                 }
    00043                 break;
    00044                 case udf_icbf_LongAd:
    00045                 {
    00046                         tp_udf_long_ad ad = (tp_udf_long_ad)(fe->ExtendedAttributes + fe->LengthofExtendedAttributes);
    00047
    00048                         *start = ad->Location.Location; // ignore partition number
    00049                         *end = *start + ((ad->Length & udf_LengthMask) - 1) / sec_size;
    00050                         return true;
    00051                 }
    00052                 break;
    00053                 case udf_icbf_ExtAd:
    00054                 {
    00055                         tp_udf_ext_ad ad = (tp_udf_ext_ad)(fe->ExtendedAttributes + fe->LengthofExtendedAttributes);
    00056
    00057                         *start = ad->Location.Location; // ignore partition number
    00058                         *end = *start + ((ad->Length & udf_LengthMask) - 1) / sec_size;
    00059                         return true;
    00060                 }
    00061                 break;
    00062         }
    00063         return false;
    00064 }
    00065
    00066 tp_udf_file udf_get_root(const HANDLE hDrive, const WORD partition_number)
    00067 {
    00068         BYTE sector[fio_SECTOR_SIZE];
    00069         tp_udf_tag tag = (tp_udf_tag)sector;
    00070         DWORD sec_size, max_sec, i, j;
    00071         DWORD MVDS_lba, MVDS_lba_end, MVDS_back_lba, MVDS_back_lba_end;
    00072         DWORD   FileDescriptorSequence_lba, FileDescriptorSequence_lba_end;
    00073         DWORD partition_lba, parent_icb;
    00074         tp_udf_AnchorVolumeDescriptorPointer avd;
    00075         bool res, part_valid, vol_valid;
    00076
    00077         if (!aspi_GetSectorInfo(hDrive, &sec_size, &max_sec))
    00078                 return NULL;
    00079
    00080         if (sec_size != fio_SECTOR_SIZE || max_sec < 256)
    00081                 return NULL;
    00082
    00083         // read AnchorVolumeDescriptorPointer at 256 (or MaxSec) (Tag == 2)
    00084         res = aspi_ReadSectors(hDrive, 256, 1, sec_size, sector);
    00085         if (!res || tag->TagIdentifier != udf_TAG_AnchorVolumeDescriptor)
    00086         {
    00087                 res = aspi_ReadSectors(hDrive, max_sec, 1, sec_size, sector);
    00088                 if (!res || tag->TagIdentifier != udf_TAG_AnchorVolumeDescriptor)
    00089                         return NULL;
    00090         }
    00091
    00092         // check Static Structures
    00093
    00094         // get MainVolumeDescriptorSequence Location & Length
    00095         avd = (tp_udf_AnchorVolumeDescriptorPointer)sector;
    00096         MVDS_lba = avd->MainVolumeDescriptorSequenceExtent.Location;
    00097         MVDS_lba_end = MVDS_lba + (avd->MainVolumeDescriptorSequenceExtent.Length - 1) / sec_size;
    00098         MVDS_back_lba = avd->ReserveVolumeDescriptorSequenceExtent.Location;
    00099         MVDS_back_lba_end = MVDS_back_lba + (avd->ReserveVolumeDescriptorSequenceExtent.Length - 1) / sec_size;
    00100
    00101         // read MVDS_Location..MVDS_Location + (MVDS_Length - 1) / SectorSize sectors
    00102
    00103         part_valid = vol_valid = false;
    00104         i = 1;
    00105         do
    00106         { // try twice (if we need to) for ReserveAnchor
    00107                 j = MVDS_lba;
    00108                 do
    00109                 {
    00110                         res = aspi_ReadSectors(hDrive, j++, 1, sec_size, sector);
    00111                         if (res)
    00112                         {
    00113                                 if (tag->TagIdentifier == udf_TAG_PartitionDescriptor && !part_valid)
    00114                                 {       // get stuff out of partition
    00115                                         tp_udf_PartitionDescriptor par = (tp_udf_PartitionDescriptor )sector;
    00116
    00117                                         part_valid = par->PartitionNumber == partition_number;
    00118                                         if (part_valid)
    00119                                         { // extract par->PartitionStartingLocation, par->PartitionLength
    00120                                                 partition_lba = par->PartitionStartingLocation;
    00121                                         }
    00122                                 }
    00123                                 else if (tag->TagIdentifier == udf_TAG_LogicalVolumeDescriptor && !vol_valid)
    00124                                 { // get stuff out of volume
    00125                                         tp_udf_LogicalVolumeDescriptor vol = (tp_udf_LogicalVolumeDescriptor)sector;
    00126
    00127                                         // check_volume sector size
    00128                                         vol_valid = (vol->LogicalBlockSize == sec_size) && (partition_number == vol->FileSetDescriptorSequence.Location.PartitionNumber);
    00129                                         if (vol_valid)
    00130                                         { // extract vol->FileSetDescriptorSequence
    00131                                                 FileDescriptorSequence_lba = vol->FileSetDescriptorSequence.Location.Location;
    00132                                                 FileDescriptorSequence_lba_end = FileDescriptorSequence_lba + ((vol->FileSetDescriptorSequence.Length & udf_LengthMask) - 1) / sec_size;
    00133                                         }
    00134                                 }
    00135                         }
    00136                         else
    00137                                 tag->TagIdentifier = 0;
    00138                 } while (j <= MVDS_lba_end && tag->TagIdentifier != udf_TAG_TerminatingDescriptor && ((!part_valid) || (!vol_valid)));
    00139
    00140                 if ((!part_valid) || (!vol_valid))
    00141                 { // try backup
    00142                         MVDS_lba = MVDS_back_lba;
    00143                         MVDS_lba_end = MVDS_back_lba_end;
    00144                 }
    00145         } while (i-- && ((!part_valid) || (!vol_valid)));
    00146
    00147         if (part_valid && vol_valid)
    00148         { // read FileSetDescriptor, get RootDir Location & Length, RootDir Length != 0
    00149                 res = aspi_ReadSectors(hDrive, FileDescriptorSequence_lba + partition_lba, 1, sec_size, sector);
    00150                 if (res && tag->TagIdentifier == udf_TAG_FileSetDescriptor)
    00151                 {
    00152                         tp_udf_FileSetDescriptor fsd = (tp_udf_FileSetDescriptor)sector;
    00153
    00154                         if (partition_number == fsd->RootDirectoryICB.Location.PartitionNumber)
    00155                         {
    00156                                 parent_icb = fsd->RootDirectoryICB.Location.Location;
    00157                                 res = aspi_ReadSectors(hDrive, partition_lba + parent_icb, 1, sec_size, sector);
    00158                                 if (res && tag->TagIdentifier == udf_TAG_FileEntry)
    00159                                 {
    00160                                         tp_udf_FileEntry fe = (tp_udf_FileEntry)sector;
    00161
    00162                                         if (fe->ICBTag.FileType == udf_FT_Directory)
    00163                                         {
    00164                                                 tp_udf_file root = (tp_udf_file)malloc(sizeof *root);
    00165
    00166                                                 root->partition_lba = partition_lba;
    00167                                                 udf_GetLBA(fe, sec_size, &root->dir_lba, &root->dir_end_lba);
    00168                                                 root->dir_left = (DWORD)fe->InformationLength; // don't want directories of more than 4gb
    00169                                                 root->sector = NULL;
    00170                                                 root->fid = NULL;
    00171                                                 root->sec_size = sec_size;
    00172                                                 strcpy(root->name, "/");
    00173                                                 root->is_dir = true;
    00174                                                 root->is_parent = false;
    00175                                                 return root;
    00176                                         }
    00177                                 }
    00178                         }
    00179                 }
    00180         }
    00181
    00182         return NULL;
    00183 }
    00184
    00185 static void udf_GetName(const BYTE *data, const DWORD len, char *target)
    00186 {
    00187         DWORD p = 1, i = 0;
    00188
    00189         if (len == 0 || !(data[0] & 0x18))
    00190                 target[0] = '\0';
    00191
    00192         if (data[0] & 0x10)
    00193         {       // ignore MSB of unicode16
    00194                 p++;
    00195
    00196                 while (p < len)
    00197                         target[i++] = data[p += 2];
    00198         }
    00199         else
    00200         {
    00201                 while (p < len)
    00202                         target[i++] = data[p++];
    00203         }
    00204
    00205         target[i]='\0';
    00206 }
    00207
    00208 tp_udf_file udf_get_sub(const HANDLE hDrive, tp_udf_file f)
    00209 {
    00210         if (f->is_dir && !f->is_parent && f->fid)
    00211         {
    00212                 BYTE sector[fio_SECTOR_SIZE];
    00213                 tp_udf_tag tag = (tp_udf_tag)sector;
    00214                 bool res;
    00215
    00216                 res = aspi_ReadSectors(hDrive, f->partition_lba + f->fid->ICB.Location.Location, 1, f->sec_size, sector);
    00217                 if (res && tag->TagIdentifier == udf_TAG_FileEntry)
    00218                 {
    00219                         tp_udf_FileEntry fe = (tp_udf_FileEntry)sector;
    00220
    00221                         if (fe->ICBTag.FileType == udf_FT_Directory)
    00222                         {
    00223                                 tp_udf_file newf = (tp_udf_file)malloc(sizeof *newf);
    00224
    00225                                 strcpy(newf->name, f->name); // maybe just ""?
    00226                                 newf->sec_size = f->sec_size;
    00227                                 newf->partition_lba = f->partition_lba;
    00228                                 udf_GetLBA(fe, f->sec_size, &newf->dir_lba, &newf->dir_end_lba);
    00229                                 newf->dir_left = (DWORD)fe->InformationLength; // don't want directories of more than 4gb
    00230                                 newf->sector = NULL;
    00231                                 newf->fid = NULL;
    00232                                 newf->is_dir = true;
    00233                                 newf->is_parent = false;
    00234                                 return newf;
    00235                         }
    00236                 }
    00237         }
    00238         return NULL;
    00239 }
    00240
    00241 tp_udf_file udf_get_next(const HANDLE hDrive, tp_udf_file f)
    00242 {
    00243         bool res = true;
    00244
    00245         if (f->dir_left <= 0)
    00246         {
    00247                 f->fid = NULL;
    00248                 return NULL;
    00249         }
    00250
    00251         if (f->fid)
    00252         { // advance to next FileIdentifierDescriptor
    00253                 DWORD ofs = 4 * ((sizeof *(f->fid) + f->fid->LengthofImplementationUse + f->fid->LengthofFileIdentifier + 3) / 4);
    00254
    00255                 f->fid = (tp_udf_FileIdentifierDescriptor)((BYTE *)f->fid + ofs);
    00256         }
    00257
    00258         if (f->fid == NULL)
    00259         {
    00260                 DWORD size = f->sec_size * (f->dir_end_lba - f->dir_lba + 1);
    00261
    00262                 if (!f->sector)
    00263                         f->sector = (BYTE*)malloc(size);
    00264                 res = aspi_ReadSectors(hDrive, f->partition_lba + f->dir_lba, (WORD)(f->dir_end_lba - f->dir_lba + 1), f->sec_size, f->sector);
    00265                 if (res)
    00266                         f->fid = (tp_udf_FileIdentifierDescriptor)f->sector;
    00267                 else
    00268                         f->fid = NULL;
    00269         }
    00270
    00271         if (f->fid && f->fid->DescriptorTag.TagIdentifier == udf_TAG_FileIdentifierDescriptor)
    00272         {
    00273                 DWORD ofs = 4 * ((sizeof *f->fid + f->fid->LengthofImplementationUse + f->fid->LengthofFileIdentifier + 3) / 4);
    00274
    00275                 f->dir_left -= ofs;
    00276                 f->is_dir = (f->fid->FileCharacteristics & udf_FID_Directory) != 0;
    00277                 f->is_parent = (f->fid->FileCharacteristics & udf_FID_Parent) != 0;
    00278                 udf_GetName(f->fid->ImplementationUse + f->fid->LengthofImplementationUse, f->fid->LengthofFileIdentifier, f->name);
    00279                 return f;
    00280         }
    00281         return NULL;
    00282 }
    00283
    00284 void udf_free(tp_udf_file f)
    00285 {
    00286         if (f)
    00287         {
    00288                 if (f->sector)
    00289                         free(f->sector);
    00290                 free(f);
    00291         }
    00292 }
    00293
    00294 #define udf_PATH_DELIMITERS "/\\"
    00295
    00296 static tp_udf_file udf_ff_traverse(const HANDLE hDrive, tp_udf_file f, char *token)
    00297 {
    00298         while (udf_get_next(hDrive, f))
    00299         {
    00300                 if (stricmp(token, f->name) == 0)
    00301                 {
    00302                         char *next_tok = strtok(NULL, udf_PATH_DELIMITERS);
    00303
    00304                         if (!next_tok)
    00305                                 return f; // found
    00306                         else if (f->is_dir)
    00307                         {
    00308                                 tp_udf_file f2 = udf_get_sub(hDrive, f);
    00309
    00310                                 if (f2)
    00311                                 {
    00312                                         tp_udf_file f3 = udf_ff_traverse(hDrive, f2, next_tok);
    00313
    00314                                         if (!f3)
    00315                                                 udf_free(f2);
    00316                                         return f3;
    00317                                 }
    00318                         }
    00319                 }
    00320         }
    00321         return NULL;
    00322 }
    00323
    00324 tp_udf_file udf_find_file(const HANDLE hDrive, const WORD partition, const char *name)
    00325 {
    00326         tp_udf_file f = udf_get_root(hDrive, partition), f2 = NULL;
    00327
    00328         if (f)
    00329         {
    00330                 char tokenline[udf_MAX_PATHLEN];
    00331                 char *token;
    00332
    00333                 strcpy(tokenline, name);
    00334                 token = strtok(tokenline, udf_PATH_DELIMITERS);
    00335                 if (token)
    00336                         f2 = udf_ff_traverse(hDrive, f, token);
    00337                 udf_free(f);
    00338         }
    00339         return f2;
    00340 }
    00341
    00342 bool udf_get_lba(const HANDLE hDrive, const tp_udf_file f, DWORD *start_lba, DWORD *end_lba)
    00343 {
    00344         if (f->fid)
    00345         {
    00346                 BYTE sector[2048];
    00347                 tp_udf_FileEntry fe = (tp_udf_FileEntry)sector;
    00348                 bool res;
    00349
    00350                 res = aspi_ReadSectors(hDrive, f->partition_lba + f->fid->ICB.Location.Location, 1, f->sec_size, sector);
    00351                 if (res && fe->DescriptorTag.TagIdentifier == udf_TAG_FileEntry)
    00352                         return udf_GetLBA(fe, f->sec_size, start_lba, end_lba);
    00353         }
    00354         return false;
    00355 }