1 /*
2 libparted
3 Copyright (C) 1998, 1999, 2000, 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 <string.h>
21
22 #include "fat.h"
23
24 #ifndef DISCOVER_ONLY
25
26 /* Note: this deals with file system start and end sectors, even if the physical
27 * devices are different (eg for fat_copy()) Perhaps this is a hack, but it
28 * works ;-)
29 */
30 static int
31 calc_deltas (FatOpContext* ctx)
32 {
33 PedFileSystem* old_fs = ctx->old_fs;
34 PedFileSystem* new_fs = ctx->new_fs;
35 FatSpecific* old_fs_info = FAT_SPECIFIC (old_fs);
36 FatSpecific* new_fs_info = FAT_SPECIFIC (new_fs);
37 PedSector old_cluster_ofs;
38 PedSector new_cluster_ofs;
39 PedSector sector_delta;
40
41 old_cluster_ofs = old_fs->geom->start + old_fs_info->cluster_offset;
42 new_cluster_ofs = new_fs->geom->start + new_fs_info->cluster_offset;
43
44 if (new_cluster_ofs > old_cluster_ofs) {
45 ctx->start_move_dir = FAT_DIR_FORWARD;
46 sector_delta = new_cluster_ofs - old_cluster_ofs;
47 } else {
48 ctx->start_move_dir = FAT_DIR_BACKWARD;
49 sector_delta = old_cluster_ofs - new_cluster_ofs;
50 }
51
52 if (sector_delta % new_fs_info->cluster_sectors) {
53 ped_exception_throw (
54 PED_EXCEPTION_BUG, PED_EXCEPTION_CANCEL,
55 _("Cluster start delta = %d, which is not a multiple "
56 "of the cluster size %d."),
57 (int) sector_delta,
58 (int) new_fs_info->cluster_sectors);
59 return 0;
60 }
61
62 ctx->start_move_delta = sector_delta / ctx->frag_sectors;
63
64 #ifdef PED_VERBOSE
65 printf ("Start move delta is: %d %s.\n",
66 (int) ctx->start_move_delta,
67 (ctx->start_move_dir == FAT_DIR_FORWARD)?
68 "forwards" : "backwards");
69 #endif
70
71 return 1;
72 }
73
74 FatOpContext*
75 fat_op_context_new (PedFileSystem* new_fs, PedFileSystem* old_fs)
76 {
77 FatSpecific* old_fs_info = FAT_SPECIFIC (old_fs);
78 FatSpecific* new_fs_info = FAT_SPECIFIC (new_fs);
79 FatOpContext* ctx;
80
81 ctx = (FatOpContext*) ped_malloc (sizeof (FatOpContext));
82 if (!ctx)
83 goto error;
84
85 ctx->frag_sectors = PED_MIN (old_fs_info->cluster_sectors,
86 new_fs_info->cluster_sectors);
87 if (!fat_set_frag_sectors (new_fs, ctx->frag_sectors))
88 goto error;
89 if (!fat_set_frag_sectors (old_fs, ctx->frag_sectors))
90 goto error;
91
92 ctx->buffer_frags = old_fs_info->buffer_sectors / ctx->frag_sectors;
93 ctx->buffer_map = (FatFragment*) ped_malloc (sizeof (FatFragment)
94 * ctx->buffer_frags);
95 if (!ctx->buffer_map)
96 goto error_free_ctx;
97
98 ctx->remap = (FatFragment*) ped_malloc (sizeof (FatFragment)
99 * old_fs_info->frag_count);
100 if (!ctx->remap)
101 goto error_free_buffer_map;
102
103 ctx->new_fs = new_fs;
104 ctx->old_fs = old_fs;
105 if (!calc_deltas (ctx))
106 goto error_free_buffer_map;
107
108 return ctx;
109
110 error_free_buffer_map:
111 ped_free (ctx->buffer_map);
112 error_free_ctx:
113 ped_free (ctx);
114 error:
115 return NULL;
116 }
117
118 void
119 fat_op_context_destroy (FatOpContext* ctx)
120 {
121 ped_free (ctx->buffer_map);
122 ped_free (ctx->remap);
123 ped_free (ctx);
124 }
125
126 FatFragment
127 fat_op_context_map_static_fragment (const FatOpContext* ctx, FatFragment frag)
128 {
129 FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs);
130 FatFragment result;
131
132 if (ctx->new_fs->geom->dev != ctx->old_fs->geom->dev)
133 return -1;
134
135 if (ctx->start_move_dir == FAT_DIR_FORWARD) {
136 if (frag < ctx->start_move_delta)
137 return -1;
138 result = frag - ctx->start_move_delta;
139 } else {
140 result = frag + ctx->start_move_delta;
141 }
142
143 if (result >= new_fs_info->frag_count)
144 return -1;
145
146 return result;
147 }
148
149 FatCluster
150 fat_op_context_map_static_cluster (const FatOpContext* ctx, FatCluster clst)
151 {
152 FatFragment mapped_frag;
153
154 mapped_frag = fat_op_context_map_static_fragment (ctx,
155 fat_cluster_to_frag (ctx->old_fs, clst));
156 if (mapped_frag != -1)
157 return fat_frag_to_cluster (ctx->new_fs, mapped_frag);
158 else
159 return 0;
160 }
161
162 FatFragment
163 fat_op_context_map_fragment (const FatOpContext* ctx, FatFragment frag)
164 {
165 return ctx->remap [frag];
166 }
167
168 FatCluster
169 fat_op_context_map_cluster (const FatOpContext* ctx, FatCluster clst)
170 {
171 FatFragment mapped_frag;
172
173 mapped_frag = fat_op_context_map_fragment (ctx,
174 fat_cluster_to_frag (ctx->old_fs, clst));
175 if (mapped_frag != -1)
176 return fat_frag_to_cluster (ctx->new_fs, mapped_frag);
177 else
178 return 0;
179 }
180
181 /* This function sets the initial fat for the new resized file system.
182 This is in *NO WAY* a proper FAT table - all it does is:
183 a) mark bad clusters as bad.
184 b) mark used clusters (that is, clusters from the original FS that are
185 reachable from the resized one). Marks as EOF (i.e. used, end of
186 file chain).
187 c) mark original file system metadata as EOF (i.e. used), to prevent
188 it from being clobbered. This will leave the original file system
189 intact, until the partition table is modified, if the start of
190 the partition is moved.
191
192 The FATs are rebuilt *properly* after cluster relocation. This here is
193 only to mark clusters as used, so when cluster relocation occurs, clusters
194 aren't relocated on top of ones marked in a, b or c.
195 */
196 int
197 fat_op_context_create_initial_fat (FatOpContext* ctx)
198 {
199 FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs);
200 FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs);
201 FatCluster clst;
202 FatCluster new_clst;
203 PedSector sect;
204 PedSector new_sect;
205 FatFragment frag;
206 FatFragment new_frag;
207 FatClusterFlag frag_flag;
208
209 new_fs_info->fat = fat_table_new (
210 new_fs_info->fat_type,
211 new_fs_info->fat_sectors * 512
212 / fat_table_entry_size (new_fs_info->fat_type));
213 if (!new_fs_info->fat)
214 return 0;
215
216 if (!fat_table_set_cluster_count (new_fs_info->fat,
217 new_fs_info->cluster_count))
218 return 0;
219
220 /* mark bad and used clusters */
221 for (frag = 0; frag < old_fs_info->frag_count; frag++) {
222 frag_flag = fat_get_fragment_flag (ctx->old_fs, frag);
223 if (frag_flag == FAT_FLAG_FREE)
224 continue;
225
226 new_frag = fat_op_context_map_static_fragment (ctx, frag);
227 if (new_frag == -1)
228 continue;
229
230 new_clst = fat_frag_to_cluster (ctx->new_fs, new_frag);
231 PED_ASSERT (new_clst != 0, return 0);
232
233 if (frag_flag == FAT_FLAG_BAD) {
234 if (!fat_table_set_bad (new_fs_info->fat, new_clst))
235 return 0;
236 } else {
237 if (!fat_table_set_eof (new_fs_info->fat, new_clst))
238 return 0;
239 }
240 }
241
242 /* mark metadata regions that map to clusters on the new FS */
243 for (sect = 0; sect < old_fs_info->cluster_offset; sect++) {
244 new_sect = ped_geometry_map (ctx->new_fs->geom,
245 ctx->old_fs->geom, sect);
246 if (new_sect == -1
247 || !fat_is_sector_in_clusters (ctx->new_fs, new_sect))
248 continue;
249
250 clst = fat_sector_to_cluster (ctx->new_fs, new_sect);
251 PED_ASSERT (clst != 0, return 0);
252
253 if (!fat_table_set_eof (new_fs_info->fat, clst))
254 return 0;
255 }
256
257 return 1;
258 }
259
260 #endif /* !DISCOVER_ONLY */