summaryrefslogtreecommitdiff
path: root/libparted/fs/r/filesys.c
blob: 6e795bccde3e400b947477b191e785c7b4fb63a8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
/* libparted - a library for manipulating disk partitions
    Copyright (C) 1999-2001, 2007-2014, 2019-2022 Free Software Foundation,
    Inc.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/** \file filesys.c */

/**
 * \addtogroup PedFileSystem
 *
 * \note File systems exist on a PedGeometry - NOT a PedPartition.
 *
 * @{
 */

#include <config.h>

#include <parted/parted.h>
#include <parted/debug.h>
#include "pt-tools.h"

#if ENABLE_NLS
#  include <libintl.h>
#  define _(String) dgettext (PACKAGE, String)
#else
#  define _(String) (String)
#endif /* ENABLE_NLS */

#define STREQ(a, b) (strcmp (a, b) == 0)

#ifndef MIN
# define MIN(a,b) (((a) < (b)) ? (a) : (b))
#endif

typedef PedFileSystem * (*open_fn_t) (PedGeometry *);
extern PedFileSystem *hfsplus_open (PedGeometry *);
extern PedFileSystem *hfs_open (PedGeometry *);
extern PedFileSystem *fat_open (PedGeometry *);

typedef int (*close_fn_t) (PedFileSystem *);
extern int hfsplus_close (PedFileSystem *);
extern int hfs_close (PedFileSystem *);
extern int fat_close (PedFileSystem *);

typedef int (*resize_fn_t) (PedFileSystem *fs, PedGeometry *geom,
			    PedTimer *timer);
extern int hfsplus_resize (PedFileSystem *fs, PedGeometry *geom,
			   PedTimer *timer);
extern int hfs_resize (PedFileSystem *fs, PedGeometry *geom,
		       PedTimer *timer);
extern int fat_resize (PedFileSystem *fs, PedGeometry *geom,
		       PedTimer *timer);

typedef PedConstraint * (*resize_constraint_fn_t) (PedFileSystem const *fs);
extern PedConstraint *hfsplus_get_resize_constraint (PedFileSystem const *fs);
extern PedConstraint *hfs_get_resize_constraint (PedFileSystem const *fs);
extern PedConstraint *fat_get_resize_constraint (PedFileSystem const *fs);

static bool
is_hfs_plus (char const *fs_type_name)
{
  return STREQ (fs_type_name, "hfsx") || STREQ (fs_type_name, "hfs+");
}

static open_fn_t
open_fn (char const *fs_type_name)
{
  if (is_hfs_plus (fs_type_name))
    return hfsplus_open;
  if (STREQ (fs_type_name, "hfs"))
    return hfs_open;
  if (strncmp (fs_type_name, "fat", 3) == 0)
    return fat_open;
  return NULL;
}

static close_fn_t
close_fn (char const *fs_type_name)
{
  if (is_hfs_plus (fs_type_name))
    return hfsplus_close;
  if (STREQ (fs_type_name, "hfs"))
    return hfs_close;
  if (strncmp (fs_type_name, "fat", 3) == 0)
    return fat_close;
  return NULL;
}

static resize_fn_t
resize_fn (char const *fs_type_name)
{
  if (is_hfs_plus (fs_type_name))
    return hfsplus_resize;
  if (STREQ (fs_type_name, "hfs"))
    return hfs_resize;
  if (strncmp (fs_type_name, "fat", 3) == 0)
    return fat_resize;
  return NULL;
}

static resize_constraint_fn_t
resize_constraint_fn (char const *fs_type_name)
{
  if (is_hfs_plus (fs_type_name))
    return hfsplus_get_resize_constraint;
  if (STREQ (fs_type_name, "hfs"))
    return hfs_get_resize_constraint;
  if (strncmp (fs_type_name, "fat", 3) == 0)
    return fat_get_resize_constraint;
  return NULL;
}

/**
 * This function opens the file system stored on \p geom, if it
 * can find one.
 * It is often called in the following manner:
 * \code
 *     fs = ped_file_system_open (&part.geom)
 * \endcode
 *
 * \throws PED_EXCEPTION_ERROR if file system could not be detected
 * \throws PED_EXCEPTION_ERROR if the file system is bigger than its volume
 * \throws PED_EXCEPTION_NO_FEATURE if opening of a file system stored on
 *     \p geom is not implemented
 *
 * \return a PedFileSystem on success, \c NULL on failure.
 */
PedFileSystem *
ped_file_system_open (PedGeometry* geom)
{
       PED_ASSERT (geom != NULL);

       if (!ped_device_open (geom->dev))
               goto error;

       PedFileSystemType *type = ped_file_system_probe (geom);
       if (!type) {
               ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
                                    _("Could not detect file system."));
               goto error_close_dev;
       }

       open_fn_t open_f = open_fn (type->name);
       if (open_f == NULL) {
	   ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
				_("resizing %s file systems is not supported"),
				type->name);
	   goto error_close_dev;
       }

       PedGeometry *probed_geom = ped_file_system_probe_specific (type, geom);
       if (!probed_geom)
               goto error_close_dev;
       if (!ped_geometry_test_inside (geom, probed_geom)) {
               if (ped_exception_throw (
                       PED_EXCEPTION_ERROR,
                       PED_EXCEPTION_IGNORE_CANCEL,
                       _("The file system is bigger than its volume!"))
                               != PED_EXCEPTION_IGNORE)
                       goto error_destroy_probed_geom;
       }

       PedFileSystem *fs = (*open_f) (probed_geom);
       if (!fs)
               goto error_destroy_probed_geom;
       ped_geometry_destroy (probed_geom);
       fs->type = type;
       return fs;

error_destroy_probed_geom:
       ped_geometry_destroy (probed_geom);
error_close_dev:
       ped_device_close (geom->dev);
error:
       return NULL;
}

/**
 * Close file system \p fs.
 *
 * \return \c 1 on success, \c 0 on failure
 */
int
ped_file_system_close (PedFileSystem* fs)
{
       PED_ASSERT (fs != NULL);
       PedDevice *dev = fs->geom->dev;
       close_fn_t fn = close_fn (fs->type->name);

       if (!fn || !(fn (fs)))
               goto error_close_dev;
       ped_device_close (dev);
       return 1;

error_close_dev:
       ped_device_close (dev);
       return 0;
}

/**
 * This function erases all file system signatures that indicate that a
 * file system occupies a given region described by \p geom.
 * After this operation ped_file_system_probe() won't detect any file system.
 *
 * \return \c 1 on success, \c 0 on failure
 */
static int
ped_file_system_clobber (PedGeometry* geom)
{
  PED_ASSERT (geom != NULL);

  if (!ped_device_open (geom->dev))
    return 0;

  /* Clear the first three and the last two sectors, albeit fewer
     when GEOM is too small.  */
  PedSector len = MIN (geom->length, geom->dev->length);

  int ok = (len <= 5
	    ? ptt_geom_clear_sectors (geom, 0, len)
	    : (ptt_geom_clear_sectors (geom, 0, 3)
	       && ptt_geom_clear_sectors (geom, geom->dev->length - 2, 2)));

  ped_device_close (geom->dev);
  return !!ok;
}

/* This function erases all signatures that indicate the presence of
 * a file system in a particular region, without erasing any data
 * contained inside the "exclude" region.
 */
static int
ped_file_system_clobber_exclude (PedGeometry* geom,
				 const PedGeometry* exclude)
{
	PedGeometry*    clobber_geom;
	int             status;

	if (ped_geometry_test_sector_inside (exclude, geom->start))
		return 1;

	clobber_geom = ped_geometry_duplicate (geom);
	if (ped_geometry_test_overlap (clobber_geom, exclude))
		ped_geometry_set_end (clobber_geom, exclude->start - 1);

	status = ped_file_system_clobber (clobber_geom);
	ped_geometry_destroy (clobber_geom);
	return status;
}

/**
 * Resize \p fs to new geometry \p geom.
 *
 * \p geom should satisfy the ped_file_system_get_resize_constraint().
 * (This isn't asserted, so it's not a bug not to... just it's likely
 * to fail ;)  If \p timer is non-NULL, it is used as the progress meter.
 *
 * \throws PED_EXCEPTION_NO_FEATURE if resizing of file system \p fs
 *     is not implemented yet
 *
 * \return \c 0 on failure
 */
int
ped_file_system_resize (PedFileSystem *fs, PedGeometry *geom, PedTimer *timer)
{
       PED_ASSERT (fs != NULL);
       PED_ASSERT (geom != NULL);

       resize_fn_t resize_f = resize_fn (fs->type->name);
       if (resize_f == NULL) {
	   ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
				_("resizing %s file systems is not supported"),
				fs->type->name);
	   return 0;
       }

       if (!ped_file_system_clobber_exclude (geom, fs->geom))
               return 0;

       return resize_f (fs, geom, timer);
}

/**
 * Return a constraint that represents all of the possible ways the
 * file system \p fs can be resized with ped_file_system_resize().
 * This takes into account the amount of used space on
 * the filesystem \p fs and the capabilities of the resize algorithm.
 * Hints:
 * -# if constraint->start_align->grain_size == 0, or
 *    constraint->start_geom->length == 1, then the start cannot be moved
 * -# constraint->min_size is the minimum size you can resize the partition
 *    to.  You might want to tell the user this ;-).
 *
 * \return a PedConstraint on success, \c NULL on failure
 */
PedConstraint *
ped_file_system_get_resize_constraint (const PedFileSystem *fs)
{
	PED_ASSERT (fs != NULL);

	resize_constraint_fn_t resize_constraint_f =
	  resize_constraint_fn (fs->type->name);
	if (resize_constraint_f == NULL)
	  return NULL;

	return resize_constraint_f (fs);
}