1 /*
2     libparted - a library for manipulating disk partitions
3     Copyright (C) 1999, 2000, 2002, 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 /* It's a bit silly calling a swap partition a file system.  Oh well...  */
20 
21 #include <config.h>
22 
23 #include <parted/parted.h>
24 #include <parted/endian.h>
25 
26 #if ENABLE_NLS
27 #  include <libintl.h>
28 #  define _(String) dgettext (PACKAGE, String)
29 #else
30 #  define _(String) (String)
31 #endif /* ENABLE_NLS */
32 
33 #include <unistd.h>
34 
35 #define SWAP_SPECIFIC(fs) ((SwapSpecific*) (fs->type_specific))
36 #define BUFFER_SIZE 128
37 
38 #define LINUXSWAP_BLOCK_SIZES       ((int[2]){512, 0})
39 
40 typedef struct {
41 	char		page_map[1];
42 } SwapOldHeader;
43 
44 /* ripped from mkswap */
45 typedef struct {
46         char            bootbits[1024];    /* Space for disklabel etc. */
47         uint32_t        version;
48         uint32_t        last_page;
49         uint32_t        nr_badpages;
50         unsigned char   sws_uuid[16];
51         unsigned char   sws_volume[16];
52         uint32_t        padding[117];
53         uint32_t        badpages[1];
54 } SwapNewHeader;
55 
56 typedef struct {
57 	union {
58 		SwapNewHeader	new;
59 		SwapOldHeader	old;
60 	}* header;
61 
62 	void*		buffer;
63 	int		buffer_size;
64 
65 	PedSector	page_sectors;
66 	unsigned int	page_count;
67 	unsigned int	version;
68 	unsigned int	max_bad_pages;
69 } SwapSpecific;
70 
71 static PedFileSystemType swap_type;
72 
73 static PedFileSystem* swap_open (PedGeometry* geom);
74 static int swap_close (PedFileSystem* fs);
75 
76 static PedGeometry*
swap_probe(PedGeometry * geom)77 swap_probe (PedGeometry* geom)
78 {
79 	PedFileSystem*	fs;
80 	SwapSpecific*	fs_info;
81 	PedGeometry*	probed_geom;
82 	PedSector	length;
83 
84 	fs = swap_open (geom);
85 	if (!fs)
86 		goto error;
87 	fs_info = SWAP_SPECIFIC (fs);
88 
89 	if (fs_info->version)
90 		length = fs_info->page_sectors * fs_info->page_count;
91 	else
92 		length = geom->length;
93 	probed_geom = ped_geometry_new (geom->dev, geom->start, length);
94 	if (!probed_geom)
95 		goto error_close_fs;
96 	swap_close (fs);
97 	return probed_geom;
98 
99 error_close_fs:
100 	swap_close (fs);
101 error:
102 	return NULL;
103 }
104 
105 #ifndef DISCOVER_ONLY
106 static int
swap_clobber(PedGeometry * geom)107 swap_clobber (PedGeometry* geom)
108 {
109 	PedFileSystem*		fs;
110 	char			buf[512];
111 
112 	fs = swap_open (geom);
113 	if (!fs)
114 		return 1;
115 
116 	memset (buf, 0, 512);
117 	if (!ped_geometry_write (geom, buf, getpagesize() / 512 - 1, 1))
118 		goto error_close_fs;
119 
120 	swap_close (fs);
121 	return 1;
122 
123 error_close_fs:
124 	swap_close (fs);
125 
126 	return 0;
127 }
128 #endif /* !DISCOVER_ONLY */
129 
130 static void
swap_init(PedFileSystem * fs,int fresh)131 swap_init (PedFileSystem* fs, int fresh)
132 {
133 	SwapSpecific*	fs_info = SWAP_SPECIFIC (fs);
134 
135 	fs_info->page_sectors = getpagesize () / 512;
136 	fs_info->page_count = fs->geom->length / fs_info->page_sectors;
137 	fs_info->version = 1;
138 	fs_info->max_bad_pages = (getpagesize()
139 					- sizeof (SwapNewHeader)) / 4;
140 
141 	if (fresh)
142 		memset (fs_info->header, 0, getpagesize());
143 	else
144 		ped_geometry_read (fs->geom, fs_info->header,
145 				   0, fs_info->page_sectors);
146 }
147 
148 static PedFileSystem*
swap_alloc(PedGeometry * geom)149 swap_alloc (PedGeometry* geom)
150 {
151 	PedFileSystem*	fs;
152 	SwapSpecific*	fs_info;
153 
154 	fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem));
155 	if (!fs)
156 		goto error;
157 
158 	fs->type_specific = (SwapSpecific*) ped_malloc (sizeof (SwapSpecific));
159 	if (!fs->type_specific)
160 		goto error_free_fs;
161 
162 	fs_info = SWAP_SPECIFIC (fs);
163 	fs_info->header = ped_malloc (getpagesize());
164 	if (!fs_info->header)
165 		goto error_free_type_specific;
166 
167 	fs_info = SWAP_SPECIFIC (fs);
168 	fs_info->buffer_size = getpagesize() * BUFFER_SIZE;
169 	fs_info->buffer = ped_malloc (fs_info->buffer_size);
170 	if (!fs_info->buffer)
171 		goto error_free_header;
172 
173 	fs->geom = ped_geometry_duplicate (geom);
174 	if (!fs->geom)
175 		goto error_free_buffer;
176 	fs->type = &swap_type;
177 	return fs;
178 
179 error_free_buffer:
180 	ped_free (fs_info->buffer);
181 error_free_header:
182 	ped_free (fs_info->header);
183 error_free_type_specific:
184 	ped_free (fs->type_specific);
185 error_free_fs:
186 	ped_free (fs);
187 error:
188 	return NULL;
189 }
190 
191 static void
swap_free(PedFileSystem * fs)192 swap_free (PedFileSystem* fs)
193 {
194 	SwapSpecific*	fs_info = SWAP_SPECIFIC (fs);
195 
196 	ped_free (fs_info->buffer);
197 	ped_free (fs_info->header);
198 	ped_free (fs->type_specific);
199 
200 	ped_geometry_destroy (fs->geom);
201 	ped_free (fs);
202 }
203 
204 static PedFileSystem*
swap_open(PedGeometry * geom)205 swap_open (PedGeometry* geom)
206 {
207 	PedFileSystem*		fs;
208 	SwapSpecific*		fs_info;
209 	const char*		sig;
210 
211 	fs = swap_alloc (geom);
212 	if (!fs)
213 		goto error;
214 	swap_init (fs, 0);
215 
216 	fs_info = SWAP_SPECIFIC (fs);
217 	if (!ped_geometry_read (fs->geom, fs_info->header, 0,
218 				fs_info->page_sectors))
219 		goto error_free_fs;
220 
221 	sig = ((char*) fs_info->header) + getpagesize() - 10;
222 	if (strncmp (sig, "SWAP-SPACE", 10) == 0) {
223 		fs_info->version = 0;
224 		fs_info->page_count
225 			= PED_MIN (fs->geom->length / fs_info->page_sectors,
226 				   8 * (getpagesize() - 10));
227 	} else if (strncmp (sig, "SWAPSPACE2", 10) == 0) {
228 		fs_info->version = 1;
229 		fs_info->page_count = fs_info->header->new.last_page;
230 	} else if (strncmp (sig, "S1SUSPEND", 9) == 0) {
231 		fs_info->version = -1;
232 	} else {
233 		char	_sig [11];
234 
235 		memcpy (_sig, sig, 10);
236 		_sig [10] = 0;
237  		ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
238 			_("Unrecognised linux swap signature '%10s'."), _sig);
239  		goto error_free_fs;
240 	}
241 
242 	fs->checked = 1;
243 	return fs;
244 
245 error_free_fs:
246 	swap_free (fs);
247 error:
248 	return NULL;
249 }
250 
251 static int
swap_close(PedFileSystem * fs)252 swap_close (PedFileSystem* fs)
253 {
254 	swap_free (fs);
255 	return 1;
256 }
257 
258 #ifndef DISCOVER_ONLY
259 static int
swap_new_find_bad_page(PedFileSystem * fs,unsigned int page)260 swap_new_find_bad_page (PedFileSystem* fs, unsigned int page)
261 {
262 	SwapSpecific*	fs_info = SWAP_SPECIFIC (fs);
263 	unsigned int	i;
264 
265 	for (i=0; i < fs_info->header->new.nr_badpages; i++) {
266 		if (fs_info->header->new.badpages [i] == page)
267 			return i;
268 	}
269 
270 	return 0;
271 }
272 
273 static int
swap_new_remove_bad_page(PedFileSystem * fs,unsigned int page)274 swap_new_remove_bad_page (PedFileSystem* fs, unsigned int page)
275 {
276 	SwapSpecific*	fs_info = SWAP_SPECIFIC (fs);
277 	unsigned int	pos;
278 
279 	pos = swap_new_find_bad_page (fs, page);
280 	if (!pos)
281 		return 0;
282 
283 	for (; pos < fs_info->header->new.nr_badpages; pos++) {
284 		fs_info->header->new.badpages [pos - 1]
285 			= fs_info->header->new.badpages [pos];
286 	}
287 
288 	return 1;
289 }
290 
291 static int
swap_mark_page(PedFileSystem * fs,unsigned int page,int ok)292 swap_mark_page (PedFileSystem* fs, unsigned int page, int ok)
293 {
294 	SwapSpecific*	fs_info = SWAP_SPECIFIC (fs);
295 	char*		ptr;
296 	unsigned int	mask;
297 
298 	if (fs_info->version == 0) {
299 		ptr = &fs_info->header->old.page_map [page/8];
300 		mask = 1 << (page%8);
301 		*ptr = (*ptr & ~mask) + ok * mask;
302 	} else {
303 		if (ok) {
304 			if (swap_new_remove_bad_page (fs, page))
305 				fs_info->header->new.nr_badpages--;
306 		} else {
307 			if (swap_new_find_bad_page (fs, page))
308 				return 1;
309 
310 			if (fs_info->header->new.nr_badpages
311 					> fs_info->max_bad_pages) {
312 				ped_exception_throw (PED_EXCEPTION_ERROR,
313 					PED_EXCEPTION_CANCEL,
314 					_("Too many bad pages."));
315 				return 0;
316 			}
317 
318 			fs_info->header->new.badpages
319 				[fs_info->header->new.nr_badpages] = page;
320 			fs_info->header->new.nr_badpages++;
321 		}
322 	}
323 
324 	return 1;
325 }
326 
327 static void
swap_clear_pages(PedFileSystem * fs)328 swap_clear_pages (PedFileSystem* fs)
329 {
330 	SwapSpecific*	fs_info = SWAP_SPECIFIC (fs);
331 	unsigned int	i;
332 
333 	for (i = 1; i < fs_info->page_count; i++) {
334 		swap_mark_page (fs, i, 1);
335 	}
336 
337 	if (fs_info->version == 0) {
338 		for (; i < 1024; i++) {
339 			swap_mark_page (fs, i, 0);
340 		}
341 	}
342 }
343 
344 static int
swap_check_pages(PedFileSystem * fs,PedTimer * timer)345 swap_check_pages (PedFileSystem* fs, PedTimer* timer)
346 {
347 	SwapSpecific*	fs_info = SWAP_SPECIFIC (fs);
348 	PedSector	result;
349 	int		first_page = 1;
350 	int		stop_page = 0;
351 	int		last_page = fs_info->page_count - 1;
352 	PedTimer*	nested_timer;
353 
354 	ped_timer_reset (timer);
355 	ped_timer_set_state_name (timer, _("checking for bad blocks"));
356 
357 	swap_clear_pages (fs);
358 	while (first_page <= last_page) {
359 		nested_timer = ped_timer_new_nested (
360 				  timer,
361 			       	  1.0 * (last_page - first_page) / last_page);
362 		result = ped_geometry_check (
363 				fs->geom,
364 				fs_info->buffer,
365 				fs_info->buffer_size / 512,
366 				first_page * fs_info->page_sectors,
367 				fs_info->page_sectors,
368 				(last_page - first_page + 1)
369 					* fs_info->page_sectors,
370 				nested_timer);
371 		ped_timer_destroy_nested (nested_timer);
372 		if (!result)
373 			return 1;
374 		stop_page = result / fs_info->page_sectors;
375 		if (!swap_mark_page (fs, stop_page, 0))
376 			return 0;
377 		first_page = stop_page + 1;
378 	}
379 	return 1;
380 }
381 
382 static int
swap_write(PedFileSystem * fs)383 swap_write (PedFileSystem* fs)
384 {
385 	SwapSpecific*	fs_info = SWAP_SPECIFIC (fs);
386 	char*		sig = ((char*) fs_info->header) + getpagesize() - 10;
387 
388 	if (fs_info->version == 0) {
389 		memcpy (sig, "SWAP-SPACE", 10);
390 	} else {
391 		fs_info->header->new.version = 1;
392 		fs_info->header->new.last_page = fs_info->page_count - 1;
393 		fs_info->header->new.nr_badpages = 0;
394 		memcpy (sig, "SWAPSPACE2", 10);
395 	}
396 
397 	return ped_geometry_write (fs->geom, fs_info->header, 0,
398 				   fs_info->page_sectors);
399 }
400 
401 static PedFileSystem*
swap_create(PedGeometry * geom,PedTimer * timer)402 swap_create (PedGeometry* geom, PedTimer* timer)
403 {
404 	PedFileSystem*		fs;
405 
406 	fs = swap_alloc (geom);
407 	if (!fs)
408 		goto error;
409 	swap_init (fs, 1);
410 	if (!swap_write (fs))
411 		goto error_free_fs;
412 	return fs;
413 
414 error_free_fs:
415 	swap_free (fs);
416 error:
417 	return NULL;
418 }
419 
420 static int
swap_resize(PedFileSystem * fs,PedGeometry * geom,PedTimer * timer)421 swap_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
422 {
423 	PedGeometry*	old_geom = fs->geom;
424 
425 	fs->geom = ped_geometry_duplicate (geom);
426 	swap_init (fs, old_geom->start != geom->start);
427 	if (!swap_write (fs))
428 		goto error;
429 	ped_geometry_destroy (old_geom);
430 	return 1;
431 
432 error:
433 	ped_geometry_destroy (fs->geom);
434 	fs->geom = old_geom;
435 	return 0;
436 }
437 
438 static PedFileSystem*
swap_copy(const PedFileSystem * fs,PedGeometry * geom,PedTimer * timer)439 swap_copy (const PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
440 {
441 	return ped_file_system_create (geom, &swap_type, timer);
442 }
443 
444 static int
swap_check(PedFileSystem * fs,PedTimer * timer)445 swap_check (PedFileSystem* fs, PedTimer* timer)
446 {
447 	return swap_check_pages (fs, timer)
448 		&& swap_write (fs);
449 }
450 
451 static PedConstraint*
swap_get_create_constraint(const PedDevice * dev)452 swap_get_create_constraint (const PedDevice* dev)
453 {
454 	PedGeometry	full_dev;
455 
456 	if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1))
457 		return NULL;
458 
459 	return ped_constraint_new (ped_alignment_any, ped_alignment_any,
460 				   &full_dev, &full_dev,
461 				   getpagesize() / 512, dev->length);
462 }
463 
464 static PedConstraint*
swap_get_resize_constraint(const PedFileSystem * fs)465 swap_get_resize_constraint (const PedFileSystem* fs)
466 {
467 	return swap_get_create_constraint (fs->geom->dev);
468 }
469 
470 static PedConstraint*
swap_get_copy_constraint(const PedFileSystem * fs,const PedDevice * dev)471 swap_get_copy_constraint (const PedFileSystem* fs, const PedDevice* dev)
472 {
473 	return swap_get_create_constraint (dev);
474 }
475 #endif /* !DISCOVER_ONLY */
476 
477 static PedFileSystemOps swap_ops = {
478 	.probe =		swap_probe,
479 #ifndef DISCOVER_ONLY
480 	.clobber =	swap_clobber,
481 	.open =		swap_open,
482 	.create =		swap_create,
483 	.close =		swap_close,
484 	.check =		swap_check,
485 	.copy =		swap_copy,
486 	.resize =		swap_resize,
487 	.get_create_constraint =	swap_get_create_constraint,
488 	.get_resize_constraint =	swap_get_resize_constraint,
489 	.get_copy_constraint =	swap_get_copy_constraint
490 #else
491 	.clobber =	NULL,
492 	.open =		NULL,
493 	.create =		NULL,
494 	.close =		NULL,
495 	.check =		NULL,
496 	.copy =		NULL,
497 	.resize =		NULL,
498 	.get_create_constraint =	NULL,
499 	.get_resize_constraint =	NULL,
500 	.get_copy_constraint =	NULL
501 #endif /* !DISCOVER_ONLY */
502 };
503 
504 static PedFileSystemType swap_type = {
505 	.next =	NULL,
506 	.ops =	&swap_ops,
507 	.name =	"linux-swap",
508 	.block_sizes = LINUXSWAP_BLOCK_SIZES
509 };
510 
511 void
ped_file_system_linux_swap_init()512 ped_file_system_linux_swap_init ()
513 {
514 	ped_file_system_type_register (&swap_type);
515 }
516 
517 void
ped_file_system_linux_swap_done()518 ped_file_system_linux_swap_done ()
519 {
520 	ped_file_system_type_unregister (&swap_type);
521 }
522 
523