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*
  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
 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
 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*
 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
 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*
 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
 252 swap_close (PedFileSystem* fs)
 253 {
 254         swap_free (fs);
 255         return 1;
 256 }
 257 
 258 #ifndef DISCOVER_ONLY
 259 static int
 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
 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
 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
 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
 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
 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*
 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
 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*
 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
 445 swap_check (PedFileSystem* fs, PedTimer* timer)
 446 {
 447         return swap_check_pages (fs, timer)
 448                 && swap_write (fs);
 449 }
 450 
 451 static PedConstraint*
 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*
 465 swap_get_resize_constraint (const PedFileSystem* fs)
 466 {
 467         return swap_get_create_constraint (fs->geom->dev);
 468 }
 469 
 470 static PedConstraint*
 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
 512 ped_file_system_linux_swap_init ()
 513 {
 514         ped_file_system_type_register (&swap_type);
 515 }
 516 
 517 void
 518 ped_file_system_linux_swap_done ()
 519 {
 520         ped_file_system_type_unregister (&swap_type);
 521 }
 522