xref: /haiku/src/add-ons/kernel/file_systems/ntfs/libntfs/ioctl.c (revision 0490778ed1bf2e39eb9d9ea8d62a2b0ab378fecd)
1*0490778eSAugustin Cavalier /**
2*0490778eSAugustin Cavalier  * ioctl.c - Processing of ioctls
3*0490778eSAugustin Cavalier  *
4*0490778eSAugustin Cavalier  *      This module is part of ntfs-3g library
5*0490778eSAugustin Cavalier  *
6*0490778eSAugustin Cavalier  * Copyright (c) 2014-2015 Jean-Pierre Andre
7*0490778eSAugustin Cavalier  * Copyright (c) 2014      Red Hat, Inc.
8*0490778eSAugustin Cavalier  *
9*0490778eSAugustin Cavalier  * This program/include file is free software; you can redistribute it and/or
10*0490778eSAugustin Cavalier  * modify it under the terms of the GNU General Public License as published
11*0490778eSAugustin Cavalier  * by the Free Software Foundation; either version 2 of the License, or
12*0490778eSAugustin Cavalier  * (at your option) any later version.
13*0490778eSAugustin Cavalier  *
14*0490778eSAugustin Cavalier  * This program/include file is distributed in the hope that it will be
15*0490778eSAugustin Cavalier  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
16*0490778eSAugustin Cavalier  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17*0490778eSAugustin Cavalier  * GNU General Public License for more details.
18*0490778eSAugustin Cavalier  *
19*0490778eSAugustin Cavalier  * You should have received a copy of the GNU General Public License
20*0490778eSAugustin Cavalier  * along with this program (in the main directory of the NTFS-3G
21*0490778eSAugustin Cavalier  * distribution in the file COPYING); if not, write to the Free Software
22*0490778eSAugustin Cavalier  * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23*0490778eSAugustin Cavalier  */
24*0490778eSAugustin Cavalier 
25*0490778eSAugustin Cavalier #include "config.h"
26*0490778eSAugustin Cavalier 
27*0490778eSAugustin Cavalier #ifdef HAVE_STDIO_H
28*0490778eSAugustin Cavalier #include <stdio.h>
29*0490778eSAugustin Cavalier #endif
30*0490778eSAugustin Cavalier #ifdef HAVE_INTTYPES_H
31*0490778eSAugustin Cavalier #include <inttypes.h>
32*0490778eSAugustin Cavalier #endif
33*0490778eSAugustin Cavalier #ifdef HAVE_STRING_H
34*0490778eSAugustin Cavalier #include <string.h>
35*0490778eSAugustin Cavalier #endif
36*0490778eSAugustin Cavalier #ifdef HAVE_ERRNO_H
37*0490778eSAugustin Cavalier #include <errno.h>
38*0490778eSAugustin Cavalier #endif
39*0490778eSAugustin Cavalier #ifdef HAVE_FCNTL_H
40*0490778eSAugustin Cavalier #include <fcntl.h>
41*0490778eSAugustin Cavalier #endif
42*0490778eSAugustin Cavalier #ifdef HAVE_UNISTD_H
43*0490778eSAugustin Cavalier #include <unistd.h>
44*0490778eSAugustin Cavalier #endif
45*0490778eSAugustin Cavalier #ifdef HAVE_STDLIB_H
46*0490778eSAugustin Cavalier #include <stdlib.h>
47*0490778eSAugustin Cavalier #endif
48*0490778eSAugustin Cavalier #ifdef HAVE_LIMITS_H
49*0490778eSAugustin Cavalier #include <limits.h>
50*0490778eSAugustin Cavalier #endif
51*0490778eSAugustin Cavalier #include <syslog.h>
52*0490778eSAugustin Cavalier #ifdef HAVE_SYS_TYPES_H
53*0490778eSAugustin Cavalier #include <sys/types.h>
54*0490778eSAugustin Cavalier #endif
55*0490778eSAugustin Cavalier #ifdef MAJOR_IN_MKDEV
56*0490778eSAugustin Cavalier #include <sys/mkdev.h>
57*0490778eSAugustin Cavalier #endif
58*0490778eSAugustin Cavalier #ifdef MAJOR_IN_SYSMACROS
59*0490778eSAugustin Cavalier #include <sys/sysmacros.h>
60*0490778eSAugustin Cavalier #endif
61*0490778eSAugustin Cavalier 
62*0490778eSAugustin Cavalier #ifdef HAVE_SYS_STAT_H
63*0490778eSAugustin Cavalier #include <sys/stat.h>
64*0490778eSAugustin Cavalier #endif
65*0490778eSAugustin Cavalier 
66*0490778eSAugustin Cavalier #ifdef HAVE_LINUX_FS_H
67*0490778eSAugustin Cavalier #include <linux/fs.h>
68*0490778eSAugustin Cavalier #endif
69*0490778eSAugustin Cavalier 
70*0490778eSAugustin Cavalier #include "compat.h"
71*0490778eSAugustin Cavalier #include "debug.h"
72*0490778eSAugustin Cavalier #include "bitmap.h"
73*0490778eSAugustin Cavalier #include "attrib.h"
74*0490778eSAugustin Cavalier #include "inode.h"
75*0490778eSAugustin Cavalier #include "layout.h"
76*0490778eSAugustin Cavalier #include "volume.h"
77*0490778eSAugustin Cavalier #include "index.h"
78*0490778eSAugustin Cavalier #include "logging.h"
79*0490778eSAugustin Cavalier #include "ntfstime.h"
80*0490778eSAugustin Cavalier #include "unistr.h"
81*0490778eSAugustin Cavalier #include "dir.h"
82*0490778eSAugustin Cavalier #include "security.h"
83*0490778eSAugustin Cavalier #include "ioctl.h"
84*0490778eSAugustin Cavalier #include "misc.h"
85*0490778eSAugustin Cavalier 
86*0490778eSAugustin Cavalier #if defined(FITRIM) && defined(BLKDISCARD)
87*0490778eSAugustin Cavalier 
88*0490778eSAugustin Cavalier /* Issue a TRIM request to the underlying device for the given clusters. */
89*0490778eSAugustin Cavalier static int fstrim_clusters(ntfs_volume *vol, LCN lcn, s64 length)
90*0490778eSAugustin Cavalier {
91*0490778eSAugustin Cavalier 	struct ntfs_device *dev = vol->dev;
92*0490778eSAugustin Cavalier 	uint64_t range[2];
93*0490778eSAugustin Cavalier 
94*0490778eSAugustin Cavalier 	ntfs_log_debug("fstrim_clusters: %lld length %lld\n",
95*0490778eSAugustin Cavalier 		(long long) lcn, (long long) length);
96*0490778eSAugustin Cavalier 
97*0490778eSAugustin Cavalier 	range[0] = lcn << vol->cluster_size_bits;
98*0490778eSAugustin Cavalier 	range[1] = length << vol->cluster_size_bits;
99*0490778eSAugustin Cavalier 
100*0490778eSAugustin Cavalier 	if (dev->d_ops->ioctl(dev, BLKDISCARD, range) == -1) {
101*0490778eSAugustin Cavalier 		ntfs_log_debug("fstrim_one_cluster: ioctl failed: %m\n");
102*0490778eSAugustin Cavalier 		return -errno;
103*0490778eSAugustin Cavalier 	}
104*0490778eSAugustin Cavalier 	return 0;
105*0490778eSAugustin Cavalier }
106*0490778eSAugustin Cavalier 
107*0490778eSAugustin Cavalier static int read_line(const char *path, char *line, size_t max_bytes)
108*0490778eSAugustin Cavalier {
109*0490778eSAugustin Cavalier 	FILE *fp;
110*0490778eSAugustin Cavalier 
111*0490778eSAugustin Cavalier 	fp = fopen(path, "r");
112*0490778eSAugustin Cavalier 	if (fp == NULL)
113*0490778eSAugustin Cavalier 		return -errno;
114*0490778eSAugustin Cavalier 	if (fgets(line, max_bytes, fp) == NULL) {
115*0490778eSAugustin Cavalier 		int ret = -EIO; /* fgets doesn't set errno */
116*0490778eSAugustin Cavalier 		fclose(fp);
117*0490778eSAugustin Cavalier 		return ret;
118*0490778eSAugustin Cavalier 	}
119*0490778eSAugustin Cavalier 	fclose (fp);
120*0490778eSAugustin Cavalier 	return 0;
121*0490778eSAugustin Cavalier }
122*0490778eSAugustin Cavalier 
123*0490778eSAugustin Cavalier static int read_u64(const char *path, u64 *n)
124*0490778eSAugustin Cavalier {
125*0490778eSAugustin Cavalier 	char line[64];
126*0490778eSAugustin Cavalier 	int ret;
127*0490778eSAugustin Cavalier 
128*0490778eSAugustin Cavalier 	ret = read_line(path, line, sizeof line);
129*0490778eSAugustin Cavalier 	if (ret)
130*0490778eSAugustin Cavalier 		return ret;
131*0490778eSAugustin Cavalier 	if (sscanf(line, "%" SCNu64, n) != 1)
132*0490778eSAugustin Cavalier 		return -EINVAL;
133*0490778eSAugustin Cavalier 	return 0;
134*0490778eSAugustin Cavalier }
135*0490778eSAugustin Cavalier 
136*0490778eSAugustin Cavalier /* Find discard limits for current backing device.
137*0490778eSAugustin Cavalier  */
138*0490778eSAugustin Cavalier static int fstrim_limits(ntfs_volume *vol,
139*0490778eSAugustin Cavalier 			u64 *discard_alignment,
140*0490778eSAugustin Cavalier 			u64 *discard_granularity,
141*0490778eSAugustin Cavalier 			u64 *discard_max_bytes)
142*0490778eSAugustin Cavalier {
143*0490778eSAugustin Cavalier 	struct stat statbuf;
144*0490778eSAugustin Cavalier 	char path1[80], path2[80];
145*0490778eSAugustin Cavalier 	int ret;
146*0490778eSAugustin Cavalier 
147*0490778eSAugustin Cavalier 	/* Stat the backing device.  Caller has ensured it is a block device. */
148*0490778eSAugustin Cavalier 	if (stat(vol->dev->d_name, &statbuf) == -1) {
149*0490778eSAugustin Cavalier 		ntfs_log_debug("fstrim_limits: could not stat %s\n",
150*0490778eSAugustin Cavalier 			vol->dev->d_name);
151*0490778eSAugustin Cavalier 		return -errno;
152*0490778eSAugustin Cavalier 	}
153*0490778eSAugustin Cavalier 
154*0490778eSAugustin Cavalier 	/* For whole devices,
155*0490778eSAugustin Cavalier 	 * /sys/dev/block/MAJOR:MINOR/discard_alignment
156*0490778eSAugustin Cavalier 	 * /sys/dev/block/MAJOR:MINOR/queue/discard_granularity
157*0490778eSAugustin Cavalier 	 * /sys/dev/block/MAJOR:MINOR/queue/discard_max_bytes
158*0490778eSAugustin Cavalier 	 * will exist.
159*0490778eSAugustin Cavalier 	 * For partitions, we also need to check the parent device:
160*0490778eSAugustin Cavalier 	 * /sys/dev/block/MAJOR:MINOR/../queue/discard_granularity
161*0490778eSAugustin Cavalier 	 * /sys/dev/block/MAJOR:MINOR/../queue/discard_max_bytes
162*0490778eSAugustin Cavalier 	 */
163*0490778eSAugustin Cavalier 	snprintf(path1, sizeof path1, "/sys/dev/block/%d:%d",
164*0490778eSAugustin Cavalier 		major(statbuf.st_rdev), minor(statbuf.st_rdev));
165*0490778eSAugustin Cavalier 
166*0490778eSAugustin Cavalier 	snprintf(path2, sizeof path2, "%s/discard_alignment", path1);
167*0490778eSAugustin Cavalier 	ret = read_u64(path2, discard_alignment);
168*0490778eSAugustin Cavalier 	if (ret) {
169*0490778eSAugustin Cavalier 		if (ret != -ENOENT)
170*0490778eSAugustin Cavalier 			return ret;
171*0490778eSAugustin Cavalier 		else
172*0490778eSAugustin Cavalier 			/* We would expect this file to exist on all
173*0490778eSAugustin Cavalier 			 * modern kernels.  But for the sake of very
174*0490778eSAugustin Cavalier 			 * old kernels:
175*0490778eSAugustin Cavalier 			 */
176*0490778eSAugustin Cavalier 			goto not_found;
177*0490778eSAugustin Cavalier 	}
178*0490778eSAugustin Cavalier 
179*0490778eSAugustin Cavalier 	snprintf(path2, sizeof path2, "%s/queue/discard_granularity", path1);
180*0490778eSAugustin Cavalier 	ret = read_u64(path2, discard_granularity);
181*0490778eSAugustin Cavalier 	if (ret) {
182*0490778eSAugustin Cavalier 		if (ret != -ENOENT)
183*0490778eSAugustin Cavalier 			return ret;
184*0490778eSAugustin Cavalier 		else {
185*0490778eSAugustin Cavalier 			snprintf(path2, sizeof path2,
186*0490778eSAugustin Cavalier 				"%s/../queue/discard_granularity", path1);
187*0490778eSAugustin Cavalier 			ret = read_u64(path2, discard_granularity);
188*0490778eSAugustin Cavalier 			if (ret) {
189*0490778eSAugustin Cavalier 				if (ret != -ENOENT)
190*0490778eSAugustin Cavalier 					return ret;
191*0490778eSAugustin Cavalier 				else
192*0490778eSAugustin Cavalier 					goto not_found;
193*0490778eSAugustin Cavalier 			}
194*0490778eSAugustin Cavalier 		}
195*0490778eSAugustin Cavalier 	}
196*0490778eSAugustin Cavalier 
197*0490778eSAugustin Cavalier 	snprintf(path2, sizeof path2, "%s/queue/discard_max_bytes", path1);
198*0490778eSAugustin Cavalier 	ret = read_u64(path2, discard_max_bytes);
199*0490778eSAugustin Cavalier 	if (ret) {
200*0490778eSAugustin Cavalier 		if (ret != -ENOENT)
201*0490778eSAugustin Cavalier 			return ret;
202*0490778eSAugustin Cavalier 		else {
203*0490778eSAugustin Cavalier 			snprintf(path2, sizeof path2,
204*0490778eSAugustin Cavalier 				"%s/../queue/discard_max_bytes", path1);
205*0490778eSAugustin Cavalier 			ret = read_u64(path2, discard_max_bytes);
206*0490778eSAugustin Cavalier 			if (ret) {
207*0490778eSAugustin Cavalier 				if (ret != -ENOENT)
208*0490778eSAugustin Cavalier 					return ret;
209*0490778eSAugustin Cavalier 				else
210*0490778eSAugustin Cavalier 					goto not_found;
211*0490778eSAugustin Cavalier 			}
212*0490778eSAugustin Cavalier 		}
213*0490778eSAugustin Cavalier 	}
214*0490778eSAugustin Cavalier 
215*0490778eSAugustin Cavalier 	return 0;
216*0490778eSAugustin Cavalier 
217*0490778eSAugustin Cavalier not_found:
218*0490778eSAugustin Cavalier 	/* If we reach here then we didn't find the device.  This is
219*0490778eSAugustin Cavalier 	 * not an error, but set discard_max_bytes = 0 to indicate
220*0490778eSAugustin Cavalier 	 * that discard is not available.
221*0490778eSAugustin Cavalier 	 */
222*0490778eSAugustin Cavalier 	*discard_alignment = 0;
223*0490778eSAugustin Cavalier 	*discard_granularity = 0;
224*0490778eSAugustin Cavalier 	*discard_max_bytes = 0;
225*0490778eSAugustin Cavalier 	return 0;
226*0490778eSAugustin Cavalier }
227*0490778eSAugustin Cavalier 
228*0490778eSAugustin Cavalier #define FSTRIM_BUFSIZ 4096
229*0490778eSAugustin Cavalier 
230*0490778eSAugustin Cavalier /* Trim the filesystem.
231*0490778eSAugustin Cavalier  *
232*0490778eSAugustin Cavalier  * Free blocks between 'start' and 'start+len-1' (both byte offsets)
233*0490778eSAugustin Cavalier  * are found and TRIM requests are sent to the block device.  'minlen'
234*0490778eSAugustin Cavalier  * is the minimum continguous free range to discard.
235*0490778eSAugustin Cavalier  */
236*0490778eSAugustin Cavalier static int fstrim(ntfs_volume *vol, void *data, u64 *trimmed)
237*0490778eSAugustin Cavalier {
238*0490778eSAugustin Cavalier 	struct fstrim_range *range = data;
239*0490778eSAugustin Cavalier 	u64 start = range->start;
240*0490778eSAugustin Cavalier 	u64 len = range->len;
241*0490778eSAugustin Cavalier 	u64 minlen = range->minlen;
242*0490778eSAugustin Cavalier 	u64 discard_alignment, discard_granularity, discard_max_bytes;
243*0490778eSAugustin Cavalier 	u8 *buf = NULL;
244*0490778eSAugustin Cavalier 	LCN start_buf;
245*0490778eSAugustin Cavalier 	int ret;
246*0490778eSAugustin Cavalier 
247*0490778eSAugustin Cavalier 	ntfs_log_debug("fstrim: start=%llu len=%llu minlen=%llu\n",
248*0490778eSAugustin Cavalier 		(unsigned long long) start,
249*0490778eSAugustin Cavalier 		(unsigned long long) len,
250*0490778eSAugustin Cavalier 		(unsigned long long) minlen);
251*0490778eSAugustin Cavalier 
252*0490778eSAugustin Cavalier 	*trimmed = 0;
253*0490778eSAugustin Cavalier 
254*0490778eSAugustin Cavalier 	/* Fail if user tries to use the fstrim -o/-l/-m options.
255*0490778eSAugustin Cavalier 	 * XXX We could fix these limitations in future.
256*0490778eSAugustin Cavalier 	 */
257*0490778eSAugustin Cavalier 	if (start != 0 || len != (uint64_t)-1) {
258*0490778eSAugustin Cavalier 		ntfs_log_debug("fstrim: setting start or length is not supported\n");
259*0490778eSAugustin Cavalier 		return -EINVAL;
260*0490778eSAugustin Cavalier 	}
261*0490778eSAugustin Cavalier 	if (minlen > vol->cluster_size) {
262*0490778eSAugustin Cavalier 		ntfs_log_debug("fstrim: minlen > cluster size is not supported\n");
263*0490778eSAugustin Cavalier 		return -EINVAL;
264*0490778eSAugustin Cavalier 	}
265*0490778eSAugustin Cavalier 
266*0490778eSAugustin Cavalier 	/* Only block devices are supported.  It would be possible to
267*0490778eSAugustin Cavalier 	 * support backing files (ie. without using loop) but the
268*0490778eSAugustin Cavalier 	 * ioctls used to punch holes in files are completely
269*0490778eSAugustin Cavalier 	 * different.
270*0490778eSAugustin Cavalier 	 */
271*0490778eSAugustin Cavalier 	if (!NDevBlock(vol->dev)) {
272*0490778eSAugustin Cavalier 		ntfs_log_debug("fstrim: not supported for non-block-device\n");
273*0490778eSAugustin Cavalier 		return -EOPNOTSUPP;
274*0490778eSAugustin Cavalier 	}
275*0490778eSAugustin Cavalier 
276*0490778eSAugustin Cavalier 	ret = fstrim_limits(vol, &discard_alignment,
277*0490778eSAugustin Cavalier 			&discard_granularity, &discard_max_bytes);
278*0490778eSAugustin Cavalier 	if (ret)
279*0490778eSAugustin Cavalier 		return ret;
280*0490778eSAugustin Cavalier 	if (discard_alignment != 0) {
281*0490778eSAugustin Cavalier 		ntfs_log_debug("fstrim: backing device is not aligned for discards\n");
282*0490778eSAugustin Cavalier 		return -EOPNOTSUPP;
283*0490778eSAugustin Cavalier 	}
284*0490778eSAugustin Cavalier 	if (discard_granularity > vol->cluster_size) {
285*0490778eSAugustin Cavalier 		ntfs_log_debug("fstrim: discard granularity of backing device is larger than cluster size\n");
286*0490778eSAugustin Cavalier 		return -EOPNOTSUPP;
287*0490778eSAugustin Cavalier 	}
288*0490778eSAugustin Cavalier 	if (discard_max_bytes == 0) {
289*0490778eSAugustin Cavalier 		ntfs_log_debug("fstrim: backing device does not support discard (discard_max_bytes == 0)\n");
290*0490778eSAugustin Cavalier 		return -EOPNOTSUPP;
291*0490778eSAugustin Cavalier 	}
292*0490778eSAugustin Cavalier 
293*0490778eSAugustin Cavalier 	/* Sync the device before doing anything. */
294*0490778eSAugustin Cavalier 	ret = ntfs_device_sync(vol->dev);
295*0490778eSAugustin Cavalier 	if (ret)
296*0490778eSAugustin Cavalier 		return ret;
297*0490778eSAugustin Cavalier 
298*0490778eSAugustin Cavalier 	/* Read through the bitmap. */
299*0490778eSAugustin Cavalier 	buf = ntfs_malloc(FSTRIM_BUFSIZ);
300*0490778eSAugustin Cavalier 	if (buf == NULL)
301*0490778eSAugustin Cavalier 		return -errno;
302*0490778eSAugustin Cavalier 	for (start_buf = 0; start_buf < vol->nr_clusters;
303*0490778eSAugustin Cavalier 	     start_buf += FSTRIM_BUFSIZ * 8) {
304*0490778eSAugustin Cavalier 		s64 count;
305*0490778eSAugustin Cavalier 		s64 br;
306*0490778eSAugustin Cavalier 		LCN end_buf, start_lcn;
307*0490778eSAugustin Cavalier 
308*0490778eSAugustin Cavalier 		/* start_buf is LCN of first cluster in the current buffer.
309*0490778eSAugustin Cavalier 		 * end_buf is LCN of last cluster + 1 in the current buffer.
310*0490778eSAugustin Cavalier 		 */
311*0490778eSAugustin Cavalier 		end_buf = start_buf + FSTRIM_BUFSIZ*8;
312*0490778eSAugustin Cavalier 		if (end_buf > vol->nr_clusters)
313*0490778eSAugustin Cavalier 			end_buf = vol->nr_clusters;
314*0490778eSAugustin Cavalier 		count = (end_buf - start_buf) / 8;
315*0490778eSAugustin Cavalier 
316*0490778eSAugustin Cavalier 		br = ntfs_attr_pread(vol->lcnbmp_na, start_buf/8, count, buf);
317*0490778eSAugustin Cavalier 		if (br != count) {
318*0490778eSAugustin Cavalier 			if (br >= 0)
319*0490778eSAugustin Cavalier 				ret = -EIO;
320*0490778eSAugustin Cavalier 			else
321*0490778eSAugustin Cavalier 				ret = -errno;
322*0490778eSAugustin Cavalier 			goto free_out;
323*0490778eSAugustin Cavalier 		}
324*0490778eSAugustin Cavalier 
325*0490778eSAugustin Cavalier 		/* Trim the clusters in large as possible blocks, but
326*0490778eSAugustin Cavalier 		 * not larger than discard_max_bytes.
327*0490778eSAugustin Cavalier 		 */
328*0490778eSAugustin Cavalier 		for (start_lcn = start_buf; start_lcn < end_buf; ++start_lcn) {
329*0490778eSAugustin Cavalier 			if (!ntfs_bit_get(buf, start_lcn-start_buf)) {
330*0490778eSAugustin Cavalier 				LCN end_lcn;
331*0490778eSAugustin Cavalier 
332*0490778eSAugustin Cavalier 				/* Cluster 'start_lcn' is not in use,
333*0490778eSAugustin Cavalier 				 * find end of this run.
334*0490778eSAugustin Cavalier 				 */
335*0490778eSAugustin Cavalier 				end_lcn = start_lcn+1;
336*0490778eSAugustin Cavalier 				while (end_lcn < end_buf &&
337*0490778eSAugustin Cavalier 					(u64) (end_lcn-start_lcn) << vol->cluster_size_bits
338*0490778eSAugustin Cavalier 					  < discard_max_bytes &&
339*0490778eSAugustin Cavalier 					!ntfs_bit_get(buf, end_lcn-start_buf))
340*0490778eSAugustin Cavalier 					end_lcn++;
341*0490778eSAugustin Cavalier 
342*0490778eSAugustin Cavalier 				ret = fstrim_clusters(vol,
343*0490778eSAugustin Cavalier 						start_lcn, end_lcn-start_lcn);
344*0490778eSAugustin Cavalier 				if (ret)
345*0490778eSAugustin Cavalier 					goto free_out;
346*0490778eSAugustin Cavalier 
347*0490778eSAugustin Cavalier 				*trimmed += (end_lcn - start_lcn)
348*0490778eSAugustin Cavalier 						<< vol->cluster_size_bits;
349*0490778eSAugustin Cavalier 				start_lcn = end_lcn-1;
350*0490778eSAugustin Cavalier 			}
351*0490778eSAugustin Cavalier 		}
352*0490778eSAugustin Cavalier 	}
353*0490778eSAugustin Cavalier 
354*0490778eSAugustin Cavalier 	ret = 0;
355*0490778eSAugustin Cavalier free_out:
356*0490778eSAugustin Cavalier 	free(buf);
357*0490778eSAugustin Cavalier 	return ret;
358*0490778eSAugustin Cavalier }
359*0490778eSAugustin Cavalier 
360*0490778eSAugustin Cavalier #endif /* FITRIM && BLKDISCARD */
361*0490778eSAugustin Cavalier 
362*0490778eSAugustin Cavalier int ntfs_ioctl(ntfs_inode *ni, int cmd, void *arg __attribute__((unused)),
363*0490778eSAugustin Cavalier 			unsigned int flags __attribute__((unused)), void *data)
364*0490778eSAugustin Cavalier {
365*0490778eSAugustin Cavalier 	int ret = 0;
366*0490778eSAugustin Cavalier 
367*0490778eSAugustin Cavalier 	switch (cmd) {
368*0490778eSAugustin Cavalier #if defined(FITRIM) && defined(BLKDISCARD)
369*0490778eSAugustin Cavalier 	case FITRIM:
370*0490778eSAugustin Cavalier 		if (!ni || !data)
371*0490778eSAugustin Cavalier 			ret = -EINVAL;
372*0490778eSAugustin Cavalier 		else {
373*0490778eSAugustin Cavalier 			u64 trimmed;
374*0490778eSAugustin Cavalier 			struct fstrim_range *range = (struct fstrim_range*)data;
375*0490778eSAugustin Cavalier 
376*0490778eSAugustin Cavalier 			ret = fstrim(ni->vol, data, &trimmed);
377*0490778eSAugustin Cavalier 			range->len = trimmed;
378*0490778eSAugustin Cavalier 		}
379*0490778eSAugustin Cavalier 		break;
380*0490778eSAugustin Cavalier #else
381*0490778eSAugustin Cavalier #warning Trimming not supported : FITRIM or BLKDISCARD not defined
382*0490778eSAugustin Cavalier #endif
383*0490778eSAugustin Cavalier 	default :
384*0490778eSAugustin Cavalier 		ret = -EINVAL;
385*0490778eSAugustin Cavalier 		break;
386*0490778eSAugustin Cavalier 	}
387*0490778eSAugustin Cavalier 	return (ret);
388*0490778eSAugustin Cavalier }
389