2 * ioctl.c - Processing of ioctls
4 * This module is part of ntfs-3g library
6 * Copyright (c) 2014-2019 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
52 #ifdef HAVE_SYS_TYPES_H
53 #include <sys/types.h>
56 #include <sys/mkdev.h>
58 #ifdef MAJOR_IN_SYSMACROS
59 #include <sys/sysmacros.h>
62 #ifdef HAVE_SYS_STAT_H
66 #ifdef HAVE_LINUX_FS_H
86 #if defined(FITRIM) && defined(BLKDISCARD)
88 /* Issue a TRIM request to the underlying device for the given clusters. */
89 static int fstrim_clusters(ntfs_volume
*vol
, LCN lcn
, s64 length
)
91 struct ntfs_device
*dev
= vol
->dev
;
94 ntfs_log_debug("fstrim_clusters: %lld length %lld\n",
95 (long long) lcn
, (long long) length
);
97 range
[0] = lcn
<< vol
->cluster_size_bits
;
98 range
[1] = length
<< vol
->cluster_size_bits
;
100 if (dev
->d_ops
->ioctl(dev
, BLKDISCARD
, range
) == -1) {
101 ntfs_log_debug("fstrim_one_cluster: ioctl failed: %m\n");
107 static int read_line(const char *path
, char *line
, size_t max_bytes
)
111 fp
= fopen(path
, "r");
114 if (fgets(line
, max_bytes
, fp
) == NULL
) {
115 int ret
= -EIO
; /* fgets doesn't set errno */
123 static int read_u64(const char *path
, u64
*n
)
128 ret
= read_line(path
, line
, sizeof line
);
131 if (sscanf(line
, "%" SCNu64
, n
) != 1)
136 /* Find discard limits for current backing device.
138 static int fstrim_limits(ntfs_volume
*vol
,
139 u64
*discard_alignment
,
140 u64
*discard_granularity
,
141 u64
*discard_max_bytes
)
144 char path1
[40]; /* holds "/sys/dev/block/%d:%d" */
145 char path2
[40 + sizeof(path1
)]; /* less than 40 bytes more than path1 */
148 /* Stat the backing device. Caller has ensured it is a block device. */
149 if (stat(vol
->dev
->d_name
, &statbuf
) == -1) {
150 ntfs_log_debug("fstrim_limits: could not stat %s\n",
155 /* For whole devices,
156 * /sys/dev/block/MAJOR:MINOR/discard_alignment
157 * /sys/dev/block/MAJOR:MINOR/queue/discard_granularity
158 * /sys/dev/block/MAJOR:MINOR/queue/discard_max_bytes
160 * For partitions, we also need to check the parent device:
161 * /sys/dev/block/MAJOR:MINOR/../queue/discard_granularity
162 * /sys/dev/block/MAJOR:MINOR/../queue/discard_max_bytes
164 snprintf(path1
, sizeof path1
, "/sys/dev/block/%d:%d",
165 major(statbuf
.st_rdev
), minor(statbuf
.st_rdev
));
167 snprintf(path2
, sizeof path2
, "%s/discard_alignment", path1
);
168 ret
= read_u64(path2
, discard_alignment
);
173 /* We would expect this file to exist on all
174 * modern kernels. But for the sake of very
180 snprintf(path2
, sizeof path2
, "%s/queue/discard_granularity", path1
);
181 ret
= read_u64(path2
, discard_granularity
);
186 snprintf(path2
, sizeof path2
,
187 "%s/../queue/discard_granularity", path1
);
188 ret
= read_u64(path2
, discard_granularity
);
198 snprintf(path2
, sizeof path2
, "%s/queue/discard_max_bytes", path1
);
199 ret
= read_u64(path2
, discard_max_bytes
);
204 snprintf(path2
, sizeof path2
,
205 "%s/../queue/discard_max_bytes", path1
);
206 ret
= read_u64(path2
, discard_max_bytes
);
219 /* If we reach here then we didn't find the device. This is
220 * not an error, but set discard_max_bytes = 0 to indicate
221 * that discard is not available.
223 *discard_alignment
= 0;
224 *discard_granularity
= 0;
225 *discard_max_bytes
= 0;
229 static inline LCN
align_up(ntfs_volume
*vol
, LCN lcn
, u64 granularity
)
233 aligned
= (lcn
<< vol
->cluster_size_bits
) + granularity
- 1;
234 aligned
-= aligned
% granularity
;
235 return (aligned
>> vol
->cluster_size_bits
);
238 static inline u64
align_down(ntfs_volume
*vol
, u64 count
, u64 granularity
)
242 aligned
= count
<< vol
->cluster_size_bits
;
243 aligned
-= aligned
% granularity
;
244 return (aligned
>> vol
->cluster_size_bits
);
247 #define FSTRIM_BUFSIZ 4096
249 /* Trim the filesystem.
251 * Free blocks between 'start' and 'start+len-1' (both byte offsets)
252 * are found and TRIM requests are sent to the block device. 'minlen'
253 * is the minimum continguous free range to discard.
255 static int fstrim(ntfs_volume
*vol
, void *data
, u64
*trimmed
)
257 struct fstrim_range
*range
= data
;
258 u64 start
= range
->start
;
259 u64 len
= range
->len
;
260 u64 minlen
= range
->minlen
;
261 u64 discard_alignment
, discard_granularity
, discard_max_bytes
;
266 ntfs_log_debug("fstrim: start=%llu len=%llu minlen=%llu\n",
267 (unsigned long long) start
,
268 (unsigned long long) len
,
269 (unsigned long long) minlen
);
273 /* Fail if user tries to use the fstrim -o/-l/-m options.
274 * XXX We could fix these limitations in future.
276 if (start
!= 0 || len
!= (uint64_t)-1) {
277 ntfs_log_error("fstrim: setting start or length is not supported\n");
280 if (minlen
> vol
->cluster_size
) {
281 ntfs_log_error("fstrim: minlen > cluster size is not supported\n");
285 /* Only block devices are supported. It would be possible to
286 * support backing files (ie. without using loop) but the
287 * ioctls used to punch holes in files are completely
290 if (!NDevBlock(vol
->dev
)) {
291 ntfs_log_error("fstrim: not supported for non-block-device\n");
295 ret
= fstrim_limits(vol
, &discard_alignment
,
296 &discard_granularity
, &discard_max_bytes
);
299 if (discard_alignment
!= 0) {
300 ntfs_log_error("fstrim: backing device is not aligned for discards\n");
304 if (discard_max_bytes
== 0) {
305 ntfs_log_error("fstrim: backing device does not support discard (discard_max_bytes == 0)\n");
309 /* Sync the device before doing anything. */
310 ret
= ntfs_device_sync(vol
->dev
);
314 /* Read through the bitmap. */
315 buf
= ntfs_malloc(FSTRIM_BUFSIZ
);
318 for (start_buf
= 0; start_buf
< vol
->nr_clusters
;
319 start_buf
+= FSTRIM_BUFSIZ
* 8) {
322 LCN end_buf
, start_lcn
;
324 /* start_buf is LCN of first cluster in the current buffer.
325 * end_buf is LCN of last cluster + 1 in the current buffer.
327 end_buf
= start_buf
+ FSTRIM_BUFSIZ
*8;
328 if (end_buf
> vol
->nr_clusters
)
329 end_buf
= vol
->nr_clusters
;
330 count
= (end_buf
- start_buf
) / 8;
332 br
= ntfs_attr_pread(vol
->lcnbmp_na
, start_buf
/8, count
, buf
);
341 /* Trim the clusters in large as possible blocks, but
342 * not larger than discard_max_bytes, and compatible
343 * with the supported trim granularity.
345 for (start_lcn
= start_buf
; start_lcn
< end_buf
; ++start_lcn
) {
346 if (!ntfs_bit_get(buf
, start_lcn
-start_buf
)) {
351 /* Cluster 'start_lcn' is not in use,
352 * find end of this run.
354 end_lcn
= start_lcn
+1;
355 while (end_lcn
< end_buf
&&
356 (u64
) (end_lcn
-start_lcn
) << vol
->cluster_size_bits
357 < discard_max_bytes
&&
358 !ntfs_bit_get(buf
, end_lcn
-start_buf
))
360 aligned_lcn
= align_up(vol
, start_lcn
,
361 discard_granularity
);
362 if (aligned_lcn
>= end_lcn
)
367 end_lcn
- aligned_lcn
,
368 discard_granularity
);
371 ret
= fstrim_clusters(vol
,
372 aligned_lcn
, aligned_count
);
376 *trimmed
+= aligned_count
377 << vol
->cluster_size_bits
;
379 start_lcn
= end_lcn
-1;
390 #endif /* FITRIM && BLKDISCARD */
392 int ntfs_ioctl(ntfs_inode
*ni
, unsigned long cmd
,
393 void *arg
__attribute__((unused
)),
394 unsigned int flags
__attribute__((unused
)), void *data
)
399 #if defined(FITRIM) && defined(BLKDISCARD)
405 struct fstrim_range
*range
= (struct fstrim_range
*)data
;
407 ret
= fstrim(ni
->vol
, data
, &trimmed
);
408 range
->len
= trimmed
;
412 #warning Trimming not supported : FITRIM or BLKDISCARD not defined