OSDN Git Service

maint: update copyrights in r/
[android-x86/external-parted.git] / libparted / fs / r / fat / resize.c
1 /*
2     libparted
3     Copyright (C) 1998-2000, 2007-2012 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 #include "count.h"
23 #include "fatio.h"
24 #include "calc.h"
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <errno.h>
32 #include <ctype.h>
33 #include <stdarg.h>
34 #include <string.h>
35
36 #ifndef DISCOVER_ONLY
37
38 /* Recursively builds (i.e. makes consistent) the duplicated directory tree
39  * (leaving the original directory tree in tact)
40  */
41 static int
42 fat_construct_directory (FatOpContext* ctx, FatTraverseInfo* trav_info)
43 {
44         FatTraverseInfo*        sub_dir_info;
45         FatDirEntry*            dir_entry;
46         FatCluster              old_first_cluster;
47
48         while ( (dir_entry = fat_traverse_next_dir_entry (trav_info)) ) {
49                 if (fat_dir_entry_is_null_term (dir_entry))
50                         break;
51                 if (!fat_dir_entry_has_first_cluster (dir_entry, ctx->old_fs))
52                         continue;
53
54                 fat_traverse_mark_dirty (trav_info);
55
56                 old_first_cluster = fat_dir_entry_get_first_cluster (dir_entry,
57                                                 ctx->old_fs);
58                 fat_dir_entry_set_first_cluster (dir_entry, ctx->new_fs,
59                         fat_op_context_map_cluster (ctx, old_first_cluster));
60
61                 if (fat_dir_entry_is_directory (dir_entry)
62                                 && dir_entry->name [0] != '.') {
63                         sub_dir_info
64                                 = fat_traverse_directory (trav_info, dir_entry);
65                         if (!sub_dir_info)
66                                 return 0;
67                         if (!fat_construct_directory (ctx, sub_dir_info))
68                                 return 0;
69                 }
70         }
71         /* remove "stale" entries at the end */
72         while ((dir_entry = fat_traverse_next_dir_entry (trav_info))) {
73                 memset (dir_entry, 0, sizeof (FatDirEntry));
74                 fat_traverse_mark_dirty (trav_info);
75         }
76         fat_traverse_complete (trav_info);
77         return 1;
78 }
79
80 static int
81 duplicate_legacy_root_dir (FatOpContext* ctx)
82 {
83         FatSpecific*            old_fs_info = FAT_SPECIFIC (ctx->old_fs);
84         FatSpecific*            new_fs_info = FAT_SPECIFIC (ctx->new_fs);
85
86         PED_ASSERT (old_fs_info->root_dir_sector_count
87                         == new_fs_info->root_dir_sector_count);
88
89         if (!ped_geometry_read (ctx->old_fs->geom, old_fs_info->buffer,
90                                 old_fs_info->root_dir_offset,
91                                 old_fs_info->root_dir_sector_count))
92                 return 0;
93
94         if (!ped_geometry_write (ctx->new_fs->geom, old_fs_info->buffer,
95                                  new_fs_info->root_dir_offset,
96                                  new_fs_info->root_dir_sector_count))
97                 return 0;
98
99         return 1;
100 }
101
102 /*
103     Constructs the new directory tree for legacy (FAT16) file systems.
104 */
105 static int
106 fat_construct_legacy_root (FatOpContext* ctx)
107 {
108         FatTraverseInfo*        trav_info;
109
110         if (!duplicate_legacy_root_dir (ctx))
111                 return 0;
112         trav_info = fat_traverse_begin (ctx->new_fs, FAT_ROOT, "\\");
113         return fat_construct_directory (ctx, trav_info);
114 }
115
116 /*
117     Constructs the new directory tree for new (FAT32) file systems.
118 */
119 static int
120 fat_construct_root (FatOpContext* ctx)
121 {
122         FatSpecific*            new_fs_info = FAT_SPECIFIC (ctx->new_fs);
123         FatTraverseInfo*        trav_info;
124
125         trav_info = fat_traverse_begin (ctx->new_fs, new_fs_info->root_cluster,
126                                         "\\");
127         fat_construct_directory (ctx, trav_info);
128         return 1;
129 }
130
131 /* Converts the root directory between FAT16 and FAT32.  NOTE: this code
132  * can also do no conversion.  I'm leaving fat_construct_directory(), because
133  * it's really pretty :-)  It also leaves a higher chance of deleted file
134  * recovery, because it doesn't remove redundant entries.  (We do this here,
135  * because brain-damaged FAT16 has an arbitary limit on root directory entries,
136  * so we save room)
137  */
138 static int
139 fat_convert_directory (FatOpContext* ctx, FatTraverseInfo* old_trav,
140                        FatTraverseInfo* new_trav)
141 {
142         FatTraverseInfo*        sub_old_dir_trav;
143         FatTraverseInfo*        sub_new_dir_trav;
144         FatDirEntry*            new_dir_entry;
145         FatDirEntry*            old_dir_entry;
146         FatCluster              old_first_cluster;
147
148         while ( (old_dir_entry = fat_traverse_next_dir_entry (old_trav)) ) {
149                 if (fat_dir_entry_is_null_term (old_dir_entry))
150                         break;
151                 if (!fat_dir_entry_is_active (old_dir_entry))
152                         continue;
153
154                 new_dir_entry = fat_traverse_next_dir_entry (new_trav);
155                 if (!new_dir_entry) {
156                         return ped_exception_throw (PED_EXCEPTION_ERROR,
157                                 PED_EXCEPTION_IGNORE_CANCEL,
158                                 _("There's not enough room in the root "
159                                   "directory for all of the files.  Either "
160                                   "cancel, or ignore to lose the files."))
161                                         == PED_EXCEPTION_IGNORE;
162                 }
163
164                 *new_dir_entry = *old_dir_entry;
165                 fat_traverse_mark_dirty (new_trav);
166
167                 if (!fat_dir_entry_has_first_cluster (old_dir_entry,
168                                                       ctx->old_fs))
169                         continue;
170
171                 old_first_cluster = fat_dir_entry_get_first_cluster (
172                                                 old_dir_entry, ctx->old_fs);
173                 fat_dir_entry_set_first_cluster (new_dir_entry, ctx->new_fs,
174                         fat_op_context_map_cluster (ctx, old_first_cluster));
175
176                 if (fat_dir_entry_is_directory (old_dir_entry)
177                                 && old_dir_entry->name [0] != '.') {
178                         sub_old_dir_trav
179                             = fat_traverse_directory (old_trav, old_dir_entry);
180                         sub_new_dir_trav
181                             = fat_traverse_directory (new_trav, new_dir_entry);
182                         if (!sub_old_dir_trav || !sub_new_dir_trav)
183                                 return 0;
184
185                         if (!fat_convert_directory (ctx, sub_old_dir_trav,
186                                                     sub_new_dir_trav))
187                                 return 0;
188                 }
189         }
190
191         /* remove "stale" entries at the end, just in case there is some
192          * overlap
193          */
194         while ((new_dir_entry = fat_traverse_next_dir_entry (new_trav))) {
195                 memset (new_dir_entry, 0, sizeof (FatDirEntry));
196                 fat_traverse_mark_dirty (new_trav);
197         }
198
199         fat_traverse_complete (old_trav);
200         fat_traverse_complete (new_trav);
201         return 1;
202 }
203
204 static void
205 clear_cluster (PedFileSystem* fs, FatCluster cluster)
206 {
207         FatSpecific*            fs_info = FAT_SPECIFIC (fs);
208
209         memset (fs_info->buffer, 0, fs_info->cluster_size);
210         fat_write_cluster (fs, fs_info->buffer, cluster);
211 }
212
213 /* This MUST be called BEFORE the fat_construct_new_fat(), because cluster
214  * allocation depend on the old FAT.  The reason is, old clusters may
215  * still be needed during the resize, (particularly clusters in the directory
216  * tree) even if they will be discarded later.
217  */
218 static int
219 alloc_root_dir (FatOpContext* ctx)
220 {
221         FatSpecific*            old_fs_info = FAT_SPECIFIC (ctx->old_fs);
222         FatSpecific*            new_fs_info = FAT_SPECIFIC (ctx->new_fs);
223         FatCluster              i;
224         FatCluster              cluster;
225         FatCluster              cluster_count;
226
227         PED_ASSERT (new_fs_info->fat_type == FAT_TYPE_FAT32);
228
229         cluster_count = ped_div_round_up (
230                            PED_MAX (16, old_fs_info->root_dir_sector_count),
231                            new_fs_info->cluster_sectors);
232
233         for (i = 0; i < cluster_count; i++) {
234                 cluster = fat_table_alloc_check_cluster (new_fs_info->fat,
235                                                          ctx->new_fs);
236                 if (!cluster)
237                         return 0;
238                 ctx->new_root_dir [i] = cluster;
239                 clear_cluster (ctx->new_fs, cluster);
240         }
241         ctx->new_root_dir [i] = 0;
242         new_fs_info->root_cluster = ctx->new_root_dir [0];
243         return 1;
244 }
245
246 /* when converting FAT32 -> FAT16
247  * fat_duplicate clusters() duplicated the root directory unnecessarily.
248  * Let's free it.
249  *
250  * This must be called AFTER fat_construct_new_fat().  (otherwise, our
251  * changes just get overwritten)
252  */
253 static int
254 free_root_dir (FatOpContext* ctx)
255 {
256         FatSpecific*            old_fs_info = FAT_SPECIFIC (ctx->old_fs);
257         FatSpecific*            new_fs_info = FAT_SPECIFIC (ctx->new_fs);
258         FatCluster              old_cluster;
259         FatFragment             i;
260
261         PED_ASSERT (old_fs_info->fat_type == FAT_TYPE_FAT32);
262         PED_ASSERT (new_fs_info->fat_type == FAT_TYPE_FAT16);
263
264         for (old_cluster = old_fs_info->root_cluster;
265              !fat_table_is_eof (old_fs_info->fat, old_cluster);
266              old_cluster = fat_table_get (old_fs_info->fat, old_cluster)) {
267                 FatFragment old_frag;
268                 old_frag = fat_cluster_to_frag (ctx->old_fs, old_cluster);
269                 for (i = 0; i < new_fs_info->cluster_frags; i++) {
270                         FatFragment new_frag;
271                         FatCluster new_clst;
272                         new_frag = fat_op_context_map_fragment (ctx,
273                                                                 old_frag + i);
274                         new_clst = fat_frag_to_cluster (ctx->old_fs, new_frag);
275                         if (!fat_table_set_avail (new_fs_info->fat, new_clst))
276                                 return 0;
277                 }
278         }
279
280         return 1;
281 }
282
283 static int
284 fat_clear_root_dir (PedFileSystem* fs)
285 {
286         FatSpecific*    fs_info = FAT_SPECIFIC (fs);
287         int             i;
288
289         PED_ASSERT (fs_info->fat_type == FAT_TYPE_FAT16);
290         PED_ASSERT (fs_info->root_dir_sector_count);
291
292         memset (fs_info->buffer, 0, 512);
293
294         for (i = 0; i < fs_info->root_dir_sector_count; i++) {
295                 if (!ped_geometry_write (fs->geom, fs_info->buffer,
296                                          fs_info->root_dir_offset + i, 1)) {
297                         if (ped_exception_throw (PED_EXCEPTION_ERROR,
298                                 PED_EXCEPTION_IGNORE_CANCEL,
299                                 _("Error writing to the root directory."))
300                                         == PED_EXCEPTION_CANCEL)
301                                 return 0;
302                 }
303         }
304         return 1;
305 }
306
307 static int
308 fat_construct_converted_tree (FatOpContext* ctx)
309 {
310         FatSpecific*            old_fs_info = FAT_SPECIFIC (ctx->old_fs);
311         FatSpecific*            new_fs_info = FAT_SPECIFIC (ctx->new_fs);
312         FatTraverseInfo*        old_trav_info;
313         FatTraverseInfo*        new_trav_info;
314
315         if (new_fs_info->fat_type == FAT_TYPE_FAT32) {
316                 new_trav_info = fat_traverse_begin (ctx->new_fs,
317                                             new_fs_info->root_cluster, "\\");
318                 old_trav_info = fat_traverse_begin (ctx->old_fs, FAT_ROOT,
319                                                     "\\");
320         } else {
321                 fat_clear_root_dir (ctx->new_fs);
322                 new_trav_info = fat_traverse_begin (ctx->new_fs, FAT_ROOT,
323                                                     "\\");
324                 old_trav_info = fat_traverse_begin (ctx->old_fs,
325                                             old_fs_info->root_cluster, "\\");
326         }
327         if (!new_trav_info || !old_trav_info)
328                 return 0;
329         if (!fat_convert_directory (ctx, old_trav_info, new_trav_info))
330                 return 0;
331         return 1;
332 }
333
334 /*
335     Constructs the new directory tree to match the new file locations.
336 */
337 static int
338 fat_construct_dir_tree (FatOpContext* ctx)
339 {
340         FatSpecific*            new_fs_info = FAT_SPECIFIC (ctx->new_fs);
341         FatSpecific*            old_fs_info = FAT_SPECIFIC (ctx->old_fs);
342
343         if (new_fs_info->fat_type == old_fs_info->fat_type) {
344                 switch (old_fs_info->fat_type) {
345                         case FAT_TYPE_FAT12:
346                         PED_ASSERT (0);
347                         break;
348
349                         case FAT_TYPE_FAT16:
350                         return fat_construct_legacy_root (ctx);
351
352                         case FAT_TYPE_FAT32:
353                         return fat_construct_root (ctx);
354                 }
355         } else {
356                 return fat_construct_converted_tree (ctx);
357         }
358
359         return 0;
360 }
361
362 static FatFragment
363 _get_next_old_frag (FatOpContext* ctx, FatFragment frag)
364 {
365         FatSpecific*    old_fs_info = FAT_SPECIFIC (ctx->old_fs);
366         FatCluster      cluster;
367         FatCluster      next_cluster;
368
369         if ((frag + 1) % old_fs_info->cluster_frags != 0) {
370                 if (fat_is_fragment_active (ctx->old_fs, frag + 1))
371                         return frag + 1;
372                 else
373                         return -1;
374         } else {
375                 cluster = fat_frag_to_cluster (ctx->old_fs, frag);
376                 next_cluster = fat_table_get (old_fs_info->fat, cluster);
377
378                 if (fat_table_is_eof (old_fs_info->fat, next_cluster))
379                         return -1;
380                 else
381                         return fat_cluster_to_frag (ctx->old_fs, next_cluster);
382         }
383 }
384
385 /*
386     Constructs the new fat for the resized file system.
387 */
388 static int
389 fat_construct_new_fat (FatOpContext* ctx)
390 {
391         FatSpecific*    old_fs_info = FAT_SPECIFIC (ctx->old_fs);
392         FatSpecific*    new_fs_info = FAT_SPECIFIC (ctx->new_fs);
393         FatFragment     old_frag;
394         FatCluster      new_cluster;
395         FatFragment     new_frag;
396         FatFragment     old_next_frag;
397         FatFragment     new_next_frag;
398         FatCluster      new_next_cluster;
399         FatClusterFlag  flag;
400         int             i;
401
402         fat_table_clear (new_fs_info->fat);
403         if (!fat_table_set_cluster_count (new_fs_info->fat,
404                                           new_fs_info->cluster_count))
405                 return 0;
406
407         for (old_frag = 0; old_frag < old_fs_info->frag_count; old_frag++) {
408                 flag = fat_get_fragment_flag (ctx->old_fs, old_frag);
409                 if (flag == FAT_FLAG_FREE)
410                         continue;
411                 if (flag == FAT_FLAG_BAD) {
412                         new_frag = fat_op_context_map_static_fragment (
413                                                 ctx, old_frag);
414                         if (new_frag == -1)
415                                 continue;
416                         new_cluster = fat_frag_to_cluster (ctx->new_fs,
417                                                            new_frag);
418                         fat_table_set_bad (new_fs_info->fat, new_cluster);
419                         continue;
420                 }
421
422                 new_frag = fat_op_context_map_fragment (ctx, old_frag);
423                 new_cluster = fat_frag_to_cluster (ctx->new_fs, new_frag);
424
425                 old_next_frag = _get_next_old_frag (ctx, old_frag);
426                 if (old_next_frag == -1) {
427                         fat_table_set_eof (new_fs_info->fat, new_cluster);
428                         continue;
429                 }
430
431                 new_next_frag = fat_op_context_map_fragment (ctx,
432                                                              old_next_frag);
433                 PED_ASSERT (new_next_frag != -1);
434
435                 new_next_cluster = fat_frag_to_cluster (ctx->new_fs,
436                                                         new_next_frag);
437                 PED_ASSERT (new_next_cluster != new_cluster);
438
439                 fat_table_set (new_fs_info->fat, new_cluster, new_next_cluster);
440         }
441
442 #if 0
443 #ifdef PED_VERBOSE
444         for (old_cluster=2; old_cluster < old_fs_info->cluster_count+2;
445              old_cluster++) {
446                 if (fat_table_is_available (old_fs_info->fat, old_cluster))
447                         continue;
448
449                 printf ("%d->%d\t(next: %d->%d)\n",
450                         old_cluster,
451                         ctx->remap [old_cluster],
452                         fat_table_get (old_fs_info->fat, old_cluster),
453                         fat_table_get (new_fs_info->fat,
454                                        ctx->remap [old_cluster]));
455         }
456 #endif /* PED_VERBOSE */
457 #endif
458
459         if (old_fs_info->fat_type == FAT_TYPE_FAT32
460             && new_fs_info->fat_type == FAT_TYPE_FAT32) {
461                 new_fs_info->root_cluster
462                         = fat_op_context_map_cluster (ctx,
463                                         old_fs_info->root_cluster);
464         }
465
466         if (old_fs_info->fat_type == FAT_TYPE_FAT16
467             && new_fs_info->fat_type == FAT_TYPE_FAT32) {
468                 for (i=0; ctx->new_root_dir[i+1]; i++) {
469                         fat_table_set (new_fs_info->fat,
470                                        ctx->new_root_dir[i],
471                                        ctx->new_root_dir[i+1]);
472                 }
473                 fat_table_set_eof (new_fs_info->fat, ctx->new_root_dir[i]);
474         }
475
476         return 1;
477 }
478
479 static int
480 ask_type (PedFileSystem* fs, int fat16_ok, int fat32_ok, FatType* out_fat_type)
481 {
482         FatSpecific*            fs_info = FAT_SPECIFIC (fs);
483         PedExceptionOption      status;
484         const char*             fat16_msg;
485         const char*             fat32_msg;
486
487         if (fs_info->fat_type == FAT_TYPE_FAT16)
488                 fat16_msg = _("If you leave your file system as FAT16, "
489                               "then you will have no problems.");
490         else
491                 fat16_msg = _("If you convert to FAT16, and MS Windows "
492                               "is installed on this partition, then "
493                               "you must re-install the MS Windows boot "
494                               "loader.  If you want to do this, you "
495                               "should consult the Parted manual (or "
496                               "your distribution's manual).");
497
498         if (fs_info->fat_type == FAT_TYPE_FAT32)
499                 fat32_msg = _("If you leave your file system as FAT32, "
500                               "then you will not introduce any new "
501                               "problems.");
502         else
503                 fat32_msg = _("If you convert to FAT32, and MS Windows "
504                               "is installed on this partition, then "
505                               "you must re-install the MS Windows boot "
506                               "loader.  If you want to do this, you "
507                               "should consult the Parted manual (or "
508                               "your distribution's manual).  Also, "
509                               "converting to FAT32 will make the file "
510                               "system unreadable by MS DOS, MS Windows "
511                               "95a, and MS Windows NT.");
512
513         if (fat16_ok && fat32_ok) {
514                 status = ped_exception_throw (
515                          PED_EXCEPTION_INFORMATION,
516                          PED_EXCEPTION_YES_NO_CANCEL,
517                          _("%s  %s  %s"),
518                          _("Would you like to use FAT32?"),
519                          fat16_msg,
520                          fat32_msg);
521
522                 switch (status) {
523                 case PED_EXCEPTION_YES:
524                         *out_fat_type = FAT_TYPE_FAT32;
525                         return 1;
526
527                 case PED_EXCEPTION_NO:
528                         *out_fat_type = FAT_TYPE_FAT16;
529                         return 1;
530
531                 case PED_EXCEPTION_UNHANDLED:
532                         *out_fat_type = fs_info->fat_type;
533                         return 1;
534
535                 case PED_EXCEPTION_CANCEL:
536                         return 0;
537
538                 default:
539                         PED_ASSERT (0);
540                         break;
541                 }
542         }
543
544         if (fat16_ok) {
545                 if (fs_info->fat_type != FAT_TYPE_FAT16) {
546                         status = ped_exception_throw (
547                                 PED_EXCEPTION_WARNING,
548                                 PED_EXCEPTION_OK_CANCEL,
549                                 _("%s  %s"),
550                                 _("The file system can only be resized to this "
551                                   "size by converting to FAT16."),
552                                 fat16_msg);
553                         if (status == PED_EXCEPTION_CANCEL)
554                                 return 0;
555                 }
556                 *out_fat_type = FAT_TYPE_FAT16;
557                 return 1;
558         }
559
560         if (fat32_ok) {
561                 if (fs_info->fat_type != FAT_TYPE_FAT32) {
562                         status = ped_exception_throw (
563                                 PED_EXCEPTION_WARNING,
564                                 PED_EXCEPTION_OK_CANCEL,
565                                 _("%s  %s"),
566                                 _("The file system can only be resized to this "
567                                   "size by converting to FAT32."),
568                                 fat32_msg);
569                         if (status == PED_EXCEPTION_CANCEL)
570                                 return 0;
571                 }
572                 *out_fat_type = FAT_TYPE_FAT32;
573                 return 1;
574         }
575
576         ped_exception_throw (
577                 PED_EXCEPTION_NO_FEATURE,
578                 PED_EXCEPTION_CANCEL,
579                 _("GNU Parted cannot resize this partition to this size.  "
580                   "We're working on it!"));
581
582         return 0;
583 }
584
585 /*  For resize operations: determine if the file system must be FAT16 or FAT32,
586  *  or either.  If the new file system must be FAT32, then query for
587  *  confirmation.  If either file system can be used, query for which one.
588  */
589 static int
590 get_fat_type (PedFileSystem* fs, const PedGeometry* new_geom,
591               FatType* out_fat_type)
592 {
593         FatSpecific*            fs_info = FAT_SPECIFIC (fs);
594         PedSector               fat16_cluster_sectors;
595         PedSector               fat32_cluster_sectors;
596         FatCluster              dummy_cluster_count;
597         PedSector               dummy_fat_sectors;
598         int                     fat16_ok;
599         int                     fat32_ok;
600
601         fat16_ok = fat_calc_resize_sizes (
602                                     new_geom,
603                                     fs_info->cluster_sectors,
604                                     FAT_TYPE_FAT16,
605                                     fs_info->root_dir_sector_count,
606                                     fs_info->cluster_sectors,
607                                     &fat16_cluster_sectors,
608                                     &dummy_cluster_count,
609                                     &dummy_fat_sectors);
610
611         fat32_ok = fat_calc_resize_sizes (
612                                     new_geom,
613                                     fs_info->cluster_sectors,
614                                     FAT_TYPE_FAT32,
615                                     fs_info->root_dir_sector_count,
616                                     fs_info->cluster_sectors,
617                                     &fat32_cluster_sectors,
618                                     &dummy_cluster_count,
619                                     &dummy_fat_sectors);
620
621         return ask_type (fs, fat16_ok, fat32_ok, out_fat_type);
622 }
623
624 /*  Creates the PedFileSystem struct for the new resized file system, and
625     sticks it in a FatOpContext.  At the end of the process, the original
626     (ctx->old_fs) is destroyed, and replaced with the new one (ctx->new_fs).
627  */
628 static FatOpContext*
629 create_resize_context (PedFileSystem* fs, const PedGeometry* new_geom)
630 {
631         FatSpecific*    fs_info = FAT_SPECIFIC (fs);
632         FatSpecific*    new_fs_info;
633         PedFileSystem*  new_fs;
634         PedSector       new_cluster_sectors;
635         FatCluster      new_cluster_count;
636         PedSector       new_fat_sectors;
637         FatType         new_fat_type;
638         PedSector       root_dir_sector_count;
639         FatOpContext*   context;
640
641         /* hypothetical number of root dir sectors, if we end up using
642          * FAT16
643          */
644         if (fs_info->root_dir_sector_count)
645                 root_dir_sector_count = fs_info->root_dir_sector_count;
646         else
647                 root_dir_sector_count = FAT_ROOT_DIR_ENTRY_COUNT
648                                                 * sizeof (FatDirEntry) / 512;
649
650         if (!get_fat_type (fs, new_geom, &new_fat_type))
651                 return 0;
652
653         fat_calc_resize_sizes (new_geom, fs_info->cluster_sectors, new_fat_type,
654                 root_dir_sector_count, fs_info->cluster_sectors,
655                 &new_cluster_sectors, &new_cluster_count, &new_fat_sectors);
656
657         if (!fat_check_resize_geometry (fs, new_geom, new_cluster_sectors,
658                                         new_cluster_count))
659                 goto error;
660
661         new_fs = fat_alloc (new_geom);
662         if (!new_fs)
663                 goto error;
664
665         new_fs_info = FAT_SPECIFIC (new_fs);
666         if (!new_fs_info)
667                 goto error_free_new_fs;
668
669 /* preserve boot code, etc. */
670         memcpy (&new_fs_info->boot_sector, &fs_info->boot_sector,
671                 sizeof (FatBootSector));
672         memcpy (&new_fs_info->info_sector, &fs_info->info_sector,
673                 sizeof (FatInfoSector));
674
675         new_fs_info->logical_sector_size = fs_info->logical_sector_size;
676         new_fs_info->sector_count = new_geom->length;
677
678         new_fs_info->sectors_per_track = fs_info->sectors_per_track;
679         new_fs_info->heads = fs_info->heads;
680
681         new_fs_info->cluster_size = new_cluster_sectors * 512;
682         new_fs_info->cluster_sectors = new_cluster_sectors;
683         new_fs_info->cluster_count = new_cluster_count;
684         new_fs_info->dir_entries_per_cluster = fs_info->dir_entries_per_cluster;
685
686         new_fs_info->fat_type = new_fat_type;
687         new_fs_info->fat_table_count = 2;
688         new_fs_info->fat_sectors = new_fat_sectors;
689
690         /* what about copying? */
691         new_fs_info->serial_number = fs_info->serial_number;
692
693         if (new_fs_info->fat_type == FAT_TYPE_FAT32) {
694                 new_fs_info->info_sector_offset = 1;
695                 new_fs_info->boot_sector_backup_offset = 6;
696
697                 new_fs_info->root_dir_offset = 0;
698                 new_fs_info->root_dir_entry_count = 0;
699                 new_fs_info->root_dir_sector_count = 0;
700
701                 /* we add calc_align_sectors to push the cluster_offset
702                    forward, to keep the clusters aligned between the new
703                    and old file systems
704                  */
705                 new_fs_info->fat_offset
706                         = fat_min_reserved_sector_count (FAT_TYPE_FAT32)
707                           + fat_calc_align_sectors (new_fs, fs);
708
709                 new_fs_info->cluster_offset
710                         = new_fs_info->fat_offset
711                           + 2 * new_fs_info->fat_sectors;
712         } else {
713                 new_fs_info->root_dir_sector_count = root_dir_sector_count;
714                 new_fs_info->root_dir_entry_count
715                         = root_dir_sector_count * 512 / sizeof (FatDirEntry);
716
717                 new_fs_info->fat_offset
718                         = fat_min_reserved_sector_count (FAT_TYPE_FAT16)
719                           + fat_calc_align_sectors (new_fs, fs);
720
721                 new_fs_info->root_dir_offset = new_fs_info->fat_offset
722                                                + 2 * new_fs_info->fat_sectors;
723
724                 new_fs_info->cluster_offset = new_fs_info->root_dir_offset
725                                           + new_fs_info->root_dir_sector_count;
726         }
727
728         new_fs_info->total_dir_clusters = fs_info->total_dir_clusters;
729
730         context = fat_op_context_new (new_fs, fs);
731         if (!context)
732                 goto error_free_new_fs_info;
733
734         if (!fat_op_context_create_initial_fat (context))
735                 goto error_free_context;
736
737         if (!fat_alloc_buffers (new_fs))
738                 goto error_free_fat;
739
740         return context;
741
742 error_free_fat:
743         fat_table_destroy (new_fs_info->fat);
744 error_free_context:
745         free (context);
746 error_free_new_fs_info:
747         free (new_fs_info);
748 error_free_new_fs:
749         free (new_fs);
750 error:
751         return NULL;
752 }
753
754 static int
755 resize_context_assimilate (FatOpContext* ctx)
756 {
757         FatSpecific*    old_fs_info = FAT_SPECIFIC (ctx->old_fs);
758         FatSpecific*    new_fs_info = FAT_SPECIFIC (ctx->new_fs);
759
760         fat_free_buffers (ctx->old_fs);
761         fat_table_destroy (old_fs_info->fat);
762         free (old_fs_info);
763         ped_geometry_destroy (ctx->old_fs->geom);
764
765         ctx->old_fs->type_specific = ctx->new_fs->type_specific;
766         ctx->old_fs->geom = ctx->new_fs->geom;
767         ctx->old_fs->type = (new_fs_info->fat_type == FAT_TYPE_FAT16)
768                                 ? &fat16_type
769                                 : &fat32_type;
770
771         free (ctx->new_fs);
772
773         fat_op_context_destroy (ctx);
774
775         return 1;
776 }
777
778 static int
779 resize_context_abort (FatOpContext* ctx)
780 {
781         FatSpecific*    new_fs_info = FAT_SPECIFIC (ctx->new_fs);
782
783         fat_free_buffers (ctx->new_fs);
784         fat_table_destroy (new_fs_info->fat);
785         free (new_fs_info);
786         ped_geometry_destroy (ctx->new_fs->geom);
787         free (ctx->new_fs);
788
789         fat_op_context_destroy (ctx);
790
791         return 1;
792 }
793
794 /* copies the "hidden" sectors, between the boot sector and the FAT.  Required,
795  * for the Windows 98 FAT32 boot loader
796  */
797 int
798 _copy_hidden_sectors (FatOpContext* ctx)
799 {
800         FatSpecific*    old_fs_info = FAT_SPECIFIC (ctx->old_fs);
801         FatSpecific*    new_fs_info = FAT_SPECIFIC (ctx->new_fs);
802         PedSector       first = 1;
803         PedSector       last;
804         PedSector       count;
805
806         /* nothing to copy for FAT16 */
807         if (old_fs_info->fat_type == FAT_TYPE_FAT16
808                         || new_fs_info->fat_type == FAT_TYPE_FAT16)
809                 return 1;
810
811         last = PED_MIN (old_fs_info->fat_offset, new_fs_info->fat_offset) - 1;
812         count = last - first + 1;
813
814         PED_ASSERT (count < BUFFER_SIZE);
815
816         if (!ped_geometry_read (ctx->old_fs->geom, old_fs_info->buffer,
817                                 first, count))
818                 return 0;
819         if (!ped_geometry_write (ctx->new_fs->geom, old_fs_info->buffer,
820                                  first, count))
821                 return 0;
822         return 1;
823 }
824
825 int
826 fat_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
827 {
828         FatSpecific*    fs_info = FAT_SPECIFIC (fs);
829         FatSpecific*    new_fs_info;
830         FatOpContext*   ctx;
831         PedFileSystem*  new_fs;
832
833         ctx = create_resize_context (fs, geom);
834         if (!ctx)
835                 goto error;
836         new_fs = ctx->new_fs;
837         new_fs_info = FAT_SPECIFIC (new_fs);
838
839         if (!fat_duplicate_clusters (ctx, timer))
840                 goto error_abort_ctx;
841         if (fs_info->fat_type == FAT_TYPE_FAT16
842                         && new_fs_info->fat_type == FAT_TYPE_FAT32) {
843                 if (!alloc_root_dir (ctx))
844                         goto error_abort_ctx;
845         }
846         if (!fat_construct_new_fat (ctx))
847                 goto error_abort_ctx;
848         if (fs_info->fat_type == FAT_TYPE_FAT32
849                         && new_fs_info->fat_type == FAT_TYPE_FAT16) {
850                 if (!free_root_dir (ctx))
851                         goto error_abort_ctx;
852         }
853         if (!fat_construct_dir_tree (ctx))
854                 goto error_abort_ctx;
855         if (!fat_table_write_all (new_fs_info->fat, new_fs))
856                 goto error_abort_ctx;
857
858         _copy_hidden_sectors (ctx);
859         fat_boot_sector_generate (&new_fs_info->boot_sector, new_fs);
860         fat_boot_sector_write (&new_fs_info->boot_sector, new_fs);
861         if (new_fs_info->fat_type == FAT_TYPE_FAT32) {
862                 fat_info_sector_generate (&new_fs_info->info_sector, new_fs);
863                 fat_info_sector_write (&new_fs_info->info_sector, new_fs);
864         }
865
866         if (!resize_context_assimilate (ctx))
867                 goto error;
868
869         return 1;
870
871 error_abort_ctx:
872         resize_context_abort (ctx);
873 error:
874         return 0;
875 }
876
877 #endif /* !DISCOVER_ONLY */