2
* ioctl.c - Processing of ioctls
4
* This module is part of ntfs-3g library
6
* Copyright (c) 2014 Jean-Pierre Andre
7
* Copyright (c) 2014 Red Hat, Inc.
9
* This program/include file is free software; you can redistribute it and/or
10
* modify it under the terms of the GNU General Public License as published
11
* by the Free Software Foundation; either version 2 of the License, or
12
* (at your option) any later version.
14
* This program/include file is distributed in the hope that it will be
15
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
16
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
* GNU General Public License for more details.
19
* You should have received a copy of the GNU General Public License
20
* along with this program (in the main directory of the NTFS-3G
21
* distribution in the file COPYING); if not, write to the Free Software
22
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30
#ifdef HAVE_INTTYPES_H
54
#include <sys/xattr.h>
57
#ifdef HAVE_SYS_TYPES_H
58
#include <sys/types.h>
61
#ifdef HAVE_SYS_STAT_H
65
#ifdef HAVE_LINUX_FS_H
85
#if defined(FITRIM) && defined(BLKDISCARD)
87
/* Issue a TRIM request to the underlying device for the given clusters. */
88
static int fstrim_clusters(ntfs_volume *vol, LCN lcn, s64 length)
90
struct ntfs_device *dev = vol->dev;
93
ntfs_log_debug("fstrim_clusters: %lld length %lld\n",
94
(long long) lcn, (long long) length);
96
range[0] = lcn << vol->cluster_size_bits;
97
range[1] = length << vol->cluster_size_bits;
99
if (dev->d_ops->ioctl(dev, BLKDISCARD, range) == -1) {
100
ntfs_log_debug("fstrim_one_cluster: ioctl failed: %m\n");
106
static int read_line(const char *path, char *line, size_t max_bytes)
110
fp = fopen(path, "r");
113
if (fgets(line, max_bytes, fp) == NULL) {
114
int ret = -EIO; /* fgets doesn't set errno */
122
static int read_u64(const char *path, u64 *n)
127
ret = read_line(path, line, sizeof line);
130
if (sscanf(line, "%" SCNu64, n) != 1)
135
/* Find discard limits for current backing device.
137
static int fstrim_limits(ntfs_volume *vol,
138
u64 *discard_alignment,
139
u64 *discard_granularity,
140
u64 *discard_max_bytes)
143
char path1[80], path2[80];
146
/* Stat the backing device. Caller has ensured it is a block device. */
147
if (stat(vol->dev->d_name, &statbuf) == -1) {
148
ntfs_log_debug("fstrim_limits: could not stat %s\n",
153
/* For whole devices,
154
* /sys/dev/block/MAJOR:MINOR/discard_alignment
155
* /sys/dev/block/MAJOR:MINOR/queue/discard_granularity
156
* /sys/dev/block/MAJOR:MINOR/queue/discard_max_bytes
158
* For partitions, we also need to check the parent device:
159
* /sys/dev/block/MAJOR:MINOR/../queue/discard_granularity
160
* /sys/dev/block/MAJOR:MINOR/../queue/discard_max_bytes
162
snprintf(path1, sizeof path1, "/sys/dev/block/%d:%d",
163
major(statbuf.st_rdev), minor(statbuf.st_rdev));
165
snprintf(path2, sizeof path2, "%s/discard_alignment", path1);
166
ret = read_u64(path2, discard_alignment);
171
/* We would expect this file to exist on all
172
* modern kernels. But for the sake of very
178
snprintf(path2, sizeof path2, "%s/queue/discard_granularity", path1);
179
ret = read_u64(path2, discard_granularity);
184
snprintf(path2, sizeof path2,
185
"%s/../queue/discard_granularity", path1);
186
ret = read_u64(path2, discard_granularity);
196
snprintf(path2, sizeof path2, "%s/queue/discard_max_bytes", path1);
197
ret = read_u64(path2, discard_max_bytes);
202
snprintf(path2, sizeof path2,
203
"%s/../queue/discard_max_bytes", path1);
204
ret = read_u64(path2, discard_max_bytes);
217
/* If we reach here then we didn't find the device. This is
218
* not an error, but set discard_max_bytes = 0 to indicate
219
* that discard is not available.
221
*discard_alignment = 0;
222
*discard_granularity = 0;
223
*discard_max_bytes = 0;
227
#define FSTRIM_BUFSIZ 4096
229
/* Trim the filesystem.
231
* Free blocks between 'start' and 'start+len-1' (both byte offsets)
232
* are found and TRIM requests are sent to the block device. 'minlen'
233
* is the minimum continguous free range to discard.
235
static int fstrim(ntfs_volume *vol, void *data)
237
struct fstrim_range *range = data;
238
u64 start = range->start;
239
u64 len = range->len;
240
u64 minlen = range->minlen;
241
u64 discard_alignment, discard_granularity, discard_max_bytes;
246
ntfs_log_debug("fstrim: start=%llu len=%llu minlen=%llu\n",
247
(unsigned long long) start,
248
(unsigned long long) len,
249
(unsigned long long) minlen);
251
/* Fail if user tries to use the fstrim -o/-l/-m options.
252
* XXX We could fix these limitations in future.
254
if (start != 0 || len != (uint64_t)-1) {
255
ntfs_log_debug("fstrim: setting start or length is not supported\n");
258
if (minlen > vol->cluster_size) {
259
ntfs_log_debug("fstrim: minlen > cluster size is not supported\n");
263
/* Only block devices are supported. It would be possible to
264
* support backing files (ie. without using loop) but the
265
* ioctls used to punch holes in files are completely
268
if (!NDevBlock(vol->dev)) {
269
ntfs_log_debug("fstrim: not supported for non-block-device\n");
273
ret = fstrim_limits(vol, &discard_alignment,
274
&discard_granularity, &discard_max_bytes);
277
if (discard_alignment != 0) {
278
ntfs_log_debug("fstrim: backing device is not aligned for discards\n");
281
if (discard_granularity > vol->cluster_size) {
282
ntfs_log_debug("fstrim: discard granularity of backing device is larger than cluster size\n");
285
if (discard_max_bytes == 0) {
286
ntfs_log_debug("fstrim: backing device does not support discard (discard_max_bytes == 0)\n");
290
/* Sync the device before doing anything. */
291
ret = ntfs_device_sync(vol->dev);
295
/* Read through the bitmap. */
296
buf = ntfs_malloc(FSTRIM_BUFSIZ);
299
for (start_buf = 0; start_buf < vol->nr_clusters;
300
start_buf += FSTRIM_BUFSIZ * 8) {
303
LCN end_buf, start_lcn;
305
/* start_buf is LCN of first cluster in the current buffer.
306
* end_buf is LCN of last cluster + 1 in the current buffer.
308
end_buf = start_buf + FSTRIM_BUFSIZ*8;
309
if (end_buf > vol->nr_clusters)
310
end_buf = vol->nr_clusters;
311
count = (end_buf - start_buf) / 8;
313
br = ntfs_attr_pread(vol->lcnbmp_na, start_buf/8, count, buf);
322
/* Trim the clusters in large as possible blocks, but
323
* not larger than discard_max_bytes.
325
for (start_lcn = start_buf; start_lcn < end_buf; ++start_lcn) {
326
if (!ntfs_bit_get(buf, start_lcn-start_buf)) {
329
/* Cluster 'start_lcn' is not in use,
330
* find end of this run.
332
end_lcn = start_lcn+1;
333
while (end_lcn < end_buf &&
334
(u64) (end_lcn-start_lcn) << vol->cluster_size_bits
335
< discard_max_bytes &&
336
!ntfs_bit_get(buf, end_lcn-start_buf))
339
ret = fstrim_clusters(vol,
340
start_lcn, end_lcn-start_lcn);
344
start_lcn = end_lcn-1;
355
#endif /* FITRIM && BLKDISCARD */
357
int ntfs_ioctl(ntfs_inode *ni, int cmd, void *arg __attribute__((unused)),
358
unsigned int flags __attribute__((unused)), void *data)
363
#if defined(FITRIM) && defined(BLKDISCARD)
368
ret = fstrim(ni->vol, data);
371
#warning FITRIM or BLKDISCARD not defined