1 /*
   2     libparted
   3     Copyright (C) 1998, 1999, 2000, 2005, 2007 Free Software Foundation, Inc.
   4 
   5     This program is free software; you can redistribute it and/or modify
   6     it under the terms of the GNU General Public License as published by
   7     the Free Software Foundation; either version 3 of the License, or
   8     (at your option) any later version.
   9 
  10     This program is distributed in the hope that it will be useful,
  11     but WITHOUT ANY WARRANTY; without even the implied warranty of
  12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13     GNU General Public License for more details.
  14 
  15     You should have received a copy of the GNU General Public License
  16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17 */
  18 
  19 #include <config.h>
  20 #include "fat.h"
  21 #include "traverse.h"
  22 
  23 #include <stdio.h>
  24 #include <stdlib.h>
  25 #include <string.h>
  26 
  27 #ifndef DISCOVER_ONLY
  28 
  29 #define NO_CLUSTER -1
  30 
  31 static char tmp_buffer [4096];
  32 
  33 int
  34 fat_traverse_entries_per_buffer (FatTraverseInfo* trav_info)
  35 {
  36         return trav_info->buffer_size / sizeof (FatDirEntry);
  37 }
  38 
  39 /* returns 1 if there are no more directory entries in the directory being
  40  * traversed, 0 otherwise.
  41  */
  42 static int
  43 is_last_buffer (FatTraverseInfo* trav_info) {
  44         FatSpecific*    fs_info = FAT_SPECIFIC (trav_info->fs);
  45     
  46         if (trav_info->is_legacy_root_dir)
  47                 return 1;
  48         else
  49                 return fat_table_is_eof (fs_info->fat, trav_info->next_buffer);
  50 }
  51 
  52 static int
  53 write_root_dir (FatTraverseInfo* trav_info)
  54 {
  55         FatSpecific*    fs_info = FAT_SPECIFIC (trav_info->fs);
  56         
  57         if (!ped_geometry_write (trav_info->fs->geom, trav_info->dir_entries,
  58                                  fs_info->root_dir_offset,
  59                                  fs_info->root_dir_sector_count))
  60                 return 0;
  61         if (!ped_geometry_sync (trav_info->fs->geom))
  62                 return 0;
  63         trav_info->dirty = 0;
  64         return 1;
  65 }
  66 
  67 static int
  68 write_dir_cluster (FatTraverseInfo* trav_info)
  69 {
  70         if (!fat_write_sync_cluster (trav_info->fs,
  71                                      (void*) trav_info->dir_entries,
  72                                      trav_info->this_buffer))
  73                 return 0;
  74         trav_info->dirty = 0;
  75         return 1;
  76 }
  77 
  78 static int
  79 write_dir_buffer (FatTraverseInfo* trav_info)
  80 {
  81         if (trav_info->is_legacy_root_dir)
  82                 return write_root_dir (trav_info);
  83         else
  84                 return write_dir_cluster (trav_info);
  85 }
  86 
  87 static int
  88 read_next_dir_buffer (FatTraverseInfo* trav_info)
  89 {
  90         FatSpecific*    fs_info = FAT_SPECIFIC (trav_info->fs);
  91 
  92         PED_ASSERT (!trav_info->is_legacy_root_dir, return 0);
  93 
  94         trav_info->this_buffer = trav_info->next_buffer;
  95 
  96         if (trav_info->this_buffer < 2
  97             || trav_info->this_buffer >= fs_info->cluster_count + 2) {
  98                 ped_exception_throw (
  99                         PED_EXCEPTION_ERROR,
 100                         PED_EXCEPTION_CANCEL,
 101                         "Cluster %ld in directory %s is outside file system!",
 102                         (long) trav_info->this_buffer,
 103                         trav_info->dir_name);
 104                 return 0;
 105         }
 106 
 107         trav_info->next_buffer
 108                 = fat_table_get (fs_info->fat, trav_info->this_buffer);
 109 
 110         return fat_read_cluster (trav_info->fs, (void *) trav_info->dir_entries,
 111                                  trav_info->this_buffer);
 112 }
 113 
 114 /* FIXME: put into fat_dir_entry_* operations */
 115 void
 116 fat_traverse_mark_dirty (FatTraverseInfo* trav_info)
 117 {
 118         trav_info->dirty = 1;
 119 }
 120 
 121 FatTraverseInfo*
 122 fat_traverse_begin (PedFileSystem* fs, FatCluster start_cluster,
 123                     char* dir_name)
 124 {
 125         FatSpecific*            fs_info = FAT_SPECIFIC (fs);
 126         FatTraverseInfo*        trav_info;
 127 
 128         trav_info = (FatTraverseInfo*) ped_malloc (sizeof (FatTraverseInfo));
 129         if (!trav_info)
 130                 goto error;
 131 
 132         trav_info->dir_name = strdup (dir_name);
 133         if (!trav_info->dir_name)
 134                 goto error_free_trav_info;
 135 
 136         trav_info->fs = fs;
 137         trav_info->is_legacy_root_dir
 138                 = (fs_info->fat_type == FAT_TYPE_FAT16) && (start_cluster == 0);
 139         trav_info->dirty = 0;
 140         trav_info->eof = 0;
 141         trav_info->current_entry = -1;
 142 
 143         if (trav_info->is_legacy_root_dir) {
 144                 trav_info->buffer_size = 512 * fs_info->root_dir_sector_count;
 145         } else {
 146                 trav_info->next_buffer = start_cluster;
 147                 trav_info->buffer_size = fs_info->cluster_size;
 148         }
 149 
 150         trav_info->dir_entries
 151                 = (FatDirEntry*) ped_malloc (trav_info->buffer_size);
 152         if (!trav_info->dir_entries)
 153                 goto error_free_dir_name;
 154 
 155         if (trav_info->is_legacy_root_dir) {
 156                 if (!ped_geometry_read (fs->geom, trav_info->dir_entries,
 157                                         fs_info->root_dir_offset,
 158                                         fs_info->root_dir_sector_count))
 159                         goto error_free_dir_entries;
 160         } else {
 161                 if (!read_next_dir_buffer (trav_info))
 162                         goto error_free_dir_entries;
 163         }
 164 
 165         return trav_info;
 166 
 167 error_free_dir_entries:
 168         ped_free (trav_info->dir_entries);
 169 error_free_dir_name:
 170         ped_free (trav_info->dir_name);
 171 error_free_trav_info:
 172         ped_free (trav_info);
 173 error:
 174         return NULL;
 175 }
 176 
 177 int
 178 fat_traverse_complete (FatTraverseInfo* trav_info)
 179 {
 180         if (trav_info->dirty) {
 181                 if (!write_dir_buffer (trav_info))
 182                         return 0;
 183         }
 184         ped_free (trav_info->dir_entries);
 185         ped_free (trav_info->dir_name);
 186         ped_free (trav_info);
 187         return 1;
 188 }
 189 
 190 FatTraverseInfo*
 191 fat_traverse_directory (FatTraverseInfo *trav_info, FatDirEntry* parent)
 192 {
 193         strcpy (tmp_buffer, trav_info->dir_name);
 194         fat_dir_entry_get_name (parent,
 195                                 tmp_buffer + strlen (trav_info->dir_name));
 196         strcat (tmp_buffer, "\\");
 197 
 198         return fat_traverse_begin (trav_info->fs,
 199                         fat_dir_entry_get_first_cluster (parent, trav_info->fs),
 200                         tmp_buffer);
 201 }
 202 
 203 FatDirEntry*
 204 fat_traverse_next_dir_entry (FatTraverseInfo *trav_info)
 205 {
 206         if (trav_info->eof)
 207                 return NULL;
 208 
 209         trav_info->current_entry++;
 210         if (trav_info->current_entry
 211                         >= fat_traverse_entries_per_buffer (trav_info)) {
 212                 if (trav_info->dirty) {
 213                         if (!write_dir_buffer (trav_info))
 214                                 return NULL;
 215                 }
 216 
 217                 trav_info->current_entry = 0;
 218                 if (is_last_buffer (trav_info)) {
 219                         trav_info->eof = 1;
 220                         return NULL;
 221                 }
 222                 if (!read_next_dir_buffer (trav_info))
 223                         return NULL;
 224         }
 225         return trav_info->dir_entries + trav_info->current_entry;
 226 }
 227 
 228 FatCluster
 229 fat_dir_entry_get_first_cluster (FatDirEntry* dir_entry, PedFileSystem *fs)
 230 {
 231         FatSpecific*            fs_info = FAT_SPECIFIC (fs);
 232 
 233         switch (fs_info->fat_type) {
 234         case FAT_TYPE_FAT12:
 235         case FAT_TYPE_FAT16:
 236                 return PED_LE16_TO_CPU (dir_entry->first_cluster);
 237 
 238         case FAT_TYPE_FAT32:
 239                 return PED_LE16_TO_CPU (dir_entry->first_cluster_high)
 240                                 * 65536L
 241                           + PED_LE16_TO_CPU (dir_entry->first_cluster);
 242         }
 243 
 244         return 0;
 245 }
 246 
 247 void
 248 fat_dir_entry_set_first_cluster (FatDirEntry* dir_entry, PedFileSystem* fs,
 249                                  FatCluster cluster)
 250 {
 251         FatSpecific*            fs_info = FAT_SPECIFIC (fs);
 252 
 253         switch (fs_info->fat_type) {
 254                 case FAT_TYPE_FAT12:
 255                 PED_ASSERT (0, (void) 0);
 256                 break;
 257 
 258                 case FAT_TYPE_FAT16:
 259                 dir_entry->first_cluster = PED_CPU_TO_LE16 (cluster);
 260                 break;
 261             
 262                 case FAT_TYPE_FAT32:
 263                 dir_entry->first_cluster
 264                         = PED_CPU_TO_LE16 (cluster & 0xffff);
 265                 dir_entry->first_cluster_high
 266                         = PED_CPU_TO_LE16 (cluster / 0x10000);
 267                 break;
 268         }
 269 }
 270 
 271 uint32_t
 272 fat_dir_entry_get_length (FatDirEntry* dir_entry)
 273 {
 274         return PED_LE32_TO_CPU (dir_entry->length);
 275 }
 276 
 277 int
 278 fat_dir_entry_is_null_term (const FatDirEntry* dir_entry)
 279 {
 280         FatDirEntry     null_entry;
 281 
 282         memset (&null_entry, 0, sizeof (null_entry));
 283         return memcmp (&null_entry, dir_entry, sizeof (null_entry)) == 0;
 284 }
 285 
 286 int
 287 fat_dir_entry_is_active (FatDirEntry* dir_entry)
 288 {
 289         if ((unsigned char) dir_entry->name[0] == DELETED_FLAG) return 0;
 290         if ((unsigned char) dir_entry->name[0] == 0) return 0;
 291         if ((unsigned char) dir_entry->name[0] == 0xF6) return 0;
 292         return 1;
 293 }
 294 
 295 int
 296 fat_dir_entry_is_file (FatDirEntry* dir_entry) {
 297         if (dir_entry->attributes == VFAT_ATTR) return 0;
 298         if (dir_entry->attributes & VOLUME_LABEL_ATTR) return 0;
 299         if (!fat_dir_entry_is_active (dir_entry)) return 0;
 300         if ((dir_entry->attributes & DIRECTORY_ATTR) == DIRECTORY_ATTR) return 0;
 301         return 1;
 302 }
 303 
 304 int
 305 fat_dir_entry_is_system_file (FatDirEntry* dir_entry)
 306 {
 307         if (!fat_dir_entry_is_file (dir_entry)) return 0;
 308         return (dir_entry->attributes & SYSTEM_ATTR)
 309                 || (dir_entry->attributes & HIDDEN_ATTR);
 310 }
 311 
 312 int
 313 fat_dir_entry_is_directory (FatDirEntry* dir_entry)
 314 {
 315         if (dir_entry->attributes == VFAT_ATTR) return 0;
 316         if (dir_entry->attributes & VOLUME_LABEL_ATTR) return 0;
 317         if (!fat_dir_entry_is_active (dir_entry)) return 0;
 318         return (dir_entry->attributes & DIRECTORY_ATTR) == DIRECTORY_ATTR;
 319 }
 320 
 321 int
 322 fat_dir_entry_has_first_cluster (FatDirEntry* dir_entry, PedFileSystem* fs)
 323 {
 324         FatSpecific*    fs_info = FAT_SPECIFIC (fs);
 325         FatCluster      first_cluster;
 326     
 327         if (!fat_dir_entry_is_file (dir_entry)
 328                 && !fat_dir_entry_is_directory (dir_entry))
 329                 return 0;
 330 
 331         first_cluster = fat_dir_entry_get_first_cluster (dir_entry, fs);
 332         if (first_cluster == 0
 333                 || fat_table_is_eof (fs_info->fat, first_cluster))
 334                 return 0;
 335 
 336         return 1;
 337 }
 338 
 339 /*
 340     decrypts silly DOS names to FILENAME.EXT
 341 */
 342 void
 343 fat_dir_entry_get_name (FatDirEntry*dir_entry, char *result) {
 344         int     i;
 345         char   *src;
 346 
 347         src = dir_entry->name;
 348 
 349         for (i=0; i<8; i++) {
 350                 if (src[i] == ' ' || src[i] == 0) break;
 351                 *result++ = src[i];
 352         }
 353 
 354         if (src[8] != ' ' && src[8] != 0) {
 355                 *result++ = '.';
 356                 for (i=8; i<11; i++) {
 357                         if (src[i] == ' ' || src[i] == 0) break;
 358                         *result++ = src[i];
 359                 }
 360         }
 361 
 362         *result = 0;
 363 }
 364 
 365 #endif /* !DISCOVER_ONLY */