1987da915Sopenharmony_ci/**
2987da915Sopenharmony_ci * ioctl.c - Processing of ioctls
3987da915Sopenharmony_ci *
4987da915Sopenharmony_ci *      This module is part of ntfs-3g library
5987da915Sopenharmony_ci *
6987da915Sopenharmony_ci * Copyright (c) 2014-2019 Jean-Pierre Andre
7987da915Sopenharmony_ci * Copyright (c) 2014      Red Hat, Inc.
8987da915Sopenharmony_ci *
9987da915Sopenharmony_ci * This program/include file is free software; you can redistribute it and/or
10987da915Sopenharmony_ci * modify it under the terms of the GNU General Public License as published
11987da915Sopenharmony_ci * by the Free Software Foundation; either version 2 of the License, or
12987da915Sopenharmony_ci * (at your option) any later version.
13987da915Sopenharmony_ci *
14987da915Sopenharmony_ci * This program/include file is distributed in the hope that it will be
15987da915Sopenharmony_ci * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
16987da915Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17987da915Sopenharmony_ci * GNU General Public License for more details.
18987da915Sopenharmony_ci *
19987da915Sopenharmony_ci * You should have received a copy of the GNU General Public License
20987da915Sopenharmony_ci * along with this program (in the main directory of the NTFS-3G
21987da915Sopenharmony_ci * distribution in the file COPYING); if not, write to the Free Software
22987da915Sopenharmony_ci * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23987da915Sopenharmony_ci */
24987da915Sopenharmony_ci
25987da915Sopenharmony_ci#include "config.h"
26987da915Sopenharmony_ci
27987da915Sopenharmony_ci#ifdef HAVE_STDIO_H
28987da915Sopenharmony_ci#include <stdio.h>
29987da915Sopenharmony_ci#endif
30987da915Sopenharmony_ci#ifdef HAVE_INTTYPES_H
31987da915Sopenharmony_ci#include <inttypes.h>
32987da915Sopenharmony_ci#endif
33987da915Sopenharmony_ci#ifdef HAVE_STRING_H
34987da915Sopenharmony_ci#include <string.h>
35987da915Sopenharmony_ci#endif
36987da915Sopenharmony_ci#ifdef HAVE_ERRNO_H
37987da915Sopenharmony_ci#include <errno.h>
38987da915Sopenharmony_ci#endif
39987da915Sopenharmony_ci#ifdef HAVE_FCNTL_H
40987da915Sopenharmony_ci#include <fcntl.h>
41987da915Sopenharmony_ci#endif
42987da915Sopenharmony_ci#ifdef HAVE_UNISTD_H
43987da915Sopenharmony_ci#include <unistd.h>
44987da915Sopenharmony_ci#endif
45987da915Sopenharmony_ci#ifdef HAVE_STDLIB_H
46987da915Sopenharmony_ci#include <stdlib.h>
47987da915Sopenharmony_ci#endif
48987da915Sopenharmony_ci#ifdef HAVE_LIMITS_H
49987da915Sopenharmony_ci#include <limits.h>
50987da915Sopenharmony_ci#endif
51987da915Sopenharmony_ci#include <syslog.h>
52987da915Sopenharmony_ci#ifdef HAVE_SYS_TYPES_H
53987da915Sopenharmony_ci#include <sys/types.h>
54987da915Sopenharmony_ci#endif
55987da915Sopenharmony_ci#ifdef MAJOR_IN_MKDEV
56987da915Sopenharmony_ci#include <sys/mkdev.h>
57987da915Sopenharmony_ci#endif
58987da915Sopenharmony_ci#ifdef MAJOR_IN_SYSMACROS
59987da915Sopenharmony_ci#include <sys/sysmacros.h>
60987da915Sopenharmony_ci#endif
61987da915Sopenharmony_ci
62987da915Sopenharmony_ci#ifdef HAVE_SYS_STAT_H
63987da915Sopenharmony_ci#include <sys/stat.h>
64987da915Sopenharmony_ci#endif
65987da915Sopenharmony_ci
66987da915Sopenharmony_ci#ifdef HAVE_LINUX_FS_H
67987da915Sopenharmony_ci#include <linux/fs.h>
68987da915Sopenharmony_ci#endif
69987da915Sopenharmony_ci
70987da915Sopenharmony_ci#include "compat.h"
71987da915Sopenharmony_ci#include "debug.h"
72987da915Sopenharmony_ci#include "bitmap.h"
73987da915Sopenharmony_ci#include "attrib.h"
74987da915Sopenharmony_ci#include "inode.h"
75987da915Sopenharmony_ci#include "layout.h"
76987da915Sopenharmony_ci#include "volume.h"
77987da915Sopenharmony_ci#include "index.h"
78987da915Sopenharmony_ci#include "logging.h"
79987da915Sopenharmony_ci#include "ntfstime.h"
80987da915Sopenharmony_ci#include "unistr.h"
81987da915Sopenharmony_ci#include "dir.h"
82987da915Sopenharmony_ci#include "security.h"
83987da915Sopenharmony_ci#include "ioctl.h"
84987da915Sopenharmony_ci#include "misc.h"
85987da915Sopenharmony_ci
86987da915Sopenharmony_ci#if defined(FITRIM) && defined(BLKDISCARD)
87987da915Sopenharmony_ci
88987da915Sopenharmony_ci/* Issue a TRIM request to the underlying device for the given clusters. */
89987da915Sopenharmony_cistatic int fstrim_clusters(ntfs_volume *vol, LCN lcn, s64 length)
90987da915Sopenharmony_ci{
91987da915Sopenharmony_ci	struct ntfs_device *dev = vol->dev;
92987da915Sopenharmony_ci	uint64_t range[2];
93987da915Sopenharmony_ci
94987da915Sopenharmony_ci	ntfs_log_debug("fstrim_clusters: %lld length %lld\n",
95987da915Sopenharmony_ci		(long long) lcn, (long long) length);
96987da915Sopenharmony_ci
97987da915Sopenharmony_ci	range[0] = lcn << vol->cluster_size_bits;
98987da915Sopenharmony_ci	range[1] = length << vol->cluster_size_bits;
99987da915Sopenharmony_ci
100987da915Sopenharmony_ci	if (dev->d_ops->ioctl(dev, BLKDISCARD, range) == -1) {
101987da915Sopenharmony_ci		ntfs_log_debug("fstrim_one_cluster: ioctl failed: %m\n");
102987da915Sopenharmony_ci		return -errno;
103987da915Sopenharmony_ci	}
104987da915Sopenharmony_ci	return 0;
105987da915Sopenharmony_ci}
106987da915Sopenharmony_ci
107987da915Sopenharmony_cistatic int read_line(const char *path, char *line, size_t max_bytes)
108987da915Sopenharmony_ci{
109987da915Sopenharmony_ci	FILE *fp;
110987da915Sopenharmony_ci
111987da915Sopenharmony_ci	fp = fopen(path, "r");
112987da915Sopenharmony_ci	if (fp == NULL)
113987da915Sopenharmony_ci		return -errno;
114987da915Sopenharmony_ci	if (fgets(line, max_bytes, fp) == NULL) {
115987da915Sopenharmony_ci		int ret = -EIO; /* fgets doesn't set errno */
116987da915Sopenharmony_ci		fclose(fp);
117987da915Sopenharmony_ci		return ret;
118987da915Sopenharmony_ci	}
119987da915Sopenharmony_ci	fclose (fp);
120987da915Sopenharmony_ci	return 0;
121987da915Sopenharmony_ci}
122987da915Sopenharmony_ci
123987da915Sopenharmony_cistatic int read_u64(const char *path, u64 *n)
124987da915Sopenharmony_ci{
125987da915Sopenharmony_ci	char line[64];
126987da915Sopenharmony_ci	int ret;
127987da915Sopenharmony_ci
128987da915Sopenharmony_ci	ret = read_line(path, line, sizeof line);
129987da915Sopenharmony_ci	if (ret)
130987da915Sopenharmony_ci		return ret;
131987da915Sopenharmony_ci	if (sscanf(line, "%" SCNu64, n) != 1)
132987da915Sopenharmony_ci		return -EINVAL;
133987da915Sopenharmony_ci	return 0;
134987da915Sopenharmony_ci}
135987da915Sopenharmony_ci
136987da915Sopenharmony_ci/* Find discard limits for current backing device.
137987da915Sopenharmony_ci */
138987da915Sopenharmony_cistatic int fstrim_limits(ntfs_volume *vol,
139987da915Sopenharmony_ci			u64 *discard_alignment,
140987da915Sopenharmony_ci			u64 *discard_granularity,
141987da915Sopenharmony_ci			u64 *discard_max_bytes)
142987da915Sopenharmony_ci{
143987da915Sopenharmony_ci	struct stat statbuf;
144987da915Sopenharmony_ci	char path1[40]; /* holds "/sys/dev/block/%d:%d" */
145987da915Sopenharmony_ci	char path2[40 + sizeof(path1)]; /* less than 40 bytes more than path1 */
146987da915Sopenharmony_ci	int ret;
147987da915Sopenharmony_ci
148987da915Sopenharmony_ci	/* Stat the backing device.  Caller has ensured it is a block device. */
149987da915Sopenharmony_ci	if (stat(vol->dev->d_name, &statbuf) == -1) {
150987da915Sopenharmony_ci		ntfs_log_debug("fstrim_limits: could not stat %s\n",
151987da915Sopenharmony_ci			vol->dev->d_name);
152987da915Sopenharmony_ci		return -errno;
153987da915Sopenharmony_ci	}
154987da915Sopenharmony_ci
155987da915Sopenharmony_ci	/* For whole devices,
156987da915Sopenharmony_ci	 * /sys/dev/block/MAJOR:MINOR/discard_alignment
157987da915Sopenharmony_ci	 * /sys/dev/block/MAJOR:MINOR/queue/discard_granularity
158987da915Sopenharmony_ci	 * /sys/dev/block/MAJOR:MINOR/queue/discard_max_bytes
159987da915Sopenharmony_ci	 * will exist.
160987da915Sopenharmony_ci	 * For partitions, we also need to check the parent device:
161987da915Sopenharmony_ci	 * /sys/dev/block/MAJOR:MINOR/../queue/discard_granularity
162987da915Sopenharmony_ci	 * /sys/dev/block/MAJOR:MINOR/../queue/discard_max_bytes
163987da915Sopenharmony_ci	 */
164987da915Sopenharmony_ci	snprintf(path1, sizeof path1, "/sys/dev/block/%d:%d",
165987da915Sopenharmony_ci		major(statbuf.st_rdev), minor(statbuf.st_rdev));
166987da915Sopenharmony_ci
167987da915Sopenharmony_ci	snprintf(path2, sizeof path2, "%s/discard_alignment", path1);
168987da915Sopenharmony_ci	ret = read_u64(path2, discard_alignment);
169987da915Sopenharmony_ci	if (ret) {
170987da915Sopenharmony_ci		if (ret != -ENOENT)
171987da915Sopenharmony_ci			return ret;
172987da915Sopenharmony_ci		else
173987da915Sopenharmony_ci			/* We would expect this file to exist on all
174987da915Sopenharmony_ci			 * modern kernels.  But for the sake of very
175987da915Sopenharmony_ci			 * old kernels:
176987da915Sopenharmony_ci			 */
177987da915Sopenharmony_ci			goto not_found;
178987da915Sopenharmony_ci	}
179987da915Sopenharmony_ci
180987da915Sopenharmony_ci	snprintf(path2, sizeof path2, "%s/queue/discard_granularity", path1);
181987da915Sopenharmony_ci	ret = read_u64(path2, discard_granularity);
182987da915Sopenharmony_ci	if (ret) {
183987da915Sopenharmony_ci		if (ret != -ENOENT)
184987da915Sopenharmony_ci			return ret;
185987da915Sopenharmony_ci		else {
186987da915Sopenharmony_ci			snprintf(path2, sizeof path2,
187987da915Sopenharmony_ci				"%s/../queue/discard_granularity", path1);
188987da915Sopenharmony_ci			ret = read_u64(path2, discard_granularity);
189987da915Sopenharmony_ci			if (ret) {
190987da915Sopenharmony_ci				if (ret != -ENOENT)
191987da915Sopenharmony_ci					return ret;
192987da915Sopenharmony_ci				else
193987da915Sopenharmony_ci					goto not_found;
194987da915Sopenharmony_ci			}
195987da915Sopenharmony_ci		}
196987da915Sopenharmony_ci	}
197987da915Sopenharmony_ci
198987da915Sopenharmony_ci	snprintf(path2, sizeof path2, "%s/queue/discard_max_bytes", path1);
199987da915Sopenharmony_ci	ret = read_u64(path2, discard_max_bytes);
200987da915Sopenharmony_ci	if (ret) {
201987da915Sopenharmony_ci		if (ret != -ENOENT)
202987da915Sopenharmony_ci			return ret;
203987da915Sopenharmony_ci		else {
204987da915Sopenharmony_ci			snprintf(path2, sizeof path2,
205987da915Sopenharmony_ci				"%s/../queue/discard_max_bytes", path1);
206987da915Sopenharmony_ci			ret = read_u64(path2, discard_max_bytes);
207987da915Sopenharmony_ci			if (ret) {
208987da915Sopenharmony_ci				if (ret != -ENOENT)
209987da915Sopenharmony_ci					return ret;
210987da915Sopenharmony_ci				else
211987da915Sopenharmony_ci					goto not_found;
212987da915Sopenharmony_ci			}
213987da915Sopenharmony_ci		}
214987da915Sopenharmony_ci	}
215987da915Sopenharmony_ci
216987da915Sopenharmony_ci	return 0;
217987da915Sopenharmony_ci
218987da915Sopenharmony_cinot_found:
219987da915Sopenharmony_ci	/* If we reach here then we didn't find the device.  This is
220987da915Sopenharmony_ci	 * not an error, but set discard_max_bytes = 0 to indicate
221987da915Sopenharmony_ci	 * that discard is not available.
222987da915Sopenharmony_ci	 */
223987da915Sopenharmony_ci	*discard_alignment = 0;
224987da915Sopenharmony_ci	*discard_granularity = 0;
225987da915Sopenharmony_ci	*discard_max_bytes = 0;
226987da915Sopenharmony_ci	return 0;
227987da915Sopenharmony_ci}
228987da915Sopenharmony_ci
229987da915Sopenharmony_cistatic inline LCN align_up(ntfs_volume *vol, LCN lcn, u64 granularity)
230987da915Sopenharmony_ci{
231987da915Sopenharmony_ci	u64 aligned;
232987da915Sopenharmony_ci
233987da915Sopenharmony_ci	aligned = (lcn << vol->cluster_size_bits) + granularity - 1;
234987da915Sopenharmony_ci	aligned -= aligned % granularity;
235987da915Sopenharmony_ci	return (aligned >> vol->cluster_size_bits);
236987da915Sopenharmony_ci}
237987da915Sopenharmony_ci
238987da915Sopenharmony_cistatic inline u64 align_down(ntfs_volume *vol, u64 count, u64 granularity)
239987da915Sopenharmony_ci{
240987da915Sopenharmony_ci	u64 aligned;
241987da915Sopenharmony_ci
242987da915Sopenharmony_ci	aligned = count << vol->cluster_size_bits;
243987da915Sopenharmony_ci	aligned -= aligned % granularity;
244987da915Sopenharmony_ci	return (aligned >> vol->cluster_size_bits);
245987da915Sopenharmony_ci}
246987da915Sopenharmony_ci
247987da915Sopenharmony_ci#define FSTRIM_BUFSIZ 4096
248987da915Sopenharmony_ci
249987da915Sopenharmony_ci/* Trim the filesystem.
250987da915Sopenharmony_ci *
251987da915Sopenharmony_ci * Free blocks between 'start' and 'start+len-1' (both byte offsets)
252987da915Sopenharmony_ci * are found and TRIM requests are sent to the block device.  'minlen'
253987da915Sopenharmony_ci * is the minimum continguous free range to discard.
254987da915Sopenharmony_ci */
255987da915Sopenharmony_cistatic int fstrim(ntfs_volume *vol, void *data, u64 *trimmed)
256987da915Sopenharmony_ci{
257987da915Sopenharmony_ci	struct fstrim_range *range = data;
258987da915Sopenharmony_ci	u64 start = range->start;
259987da915Sopenharmony_ci	u64 len = range->len;
260987da915Sopenharmony_ci	u64 minlen = range->minlen;
261987da915Sopenharmony_ci	u64 discard_alignment, discard_granularity, discard_max_bytes;
262987da915Sopenharmony_ci	u8 *buf = NULL;
263987da915Sopenharmony_ci	LCN start_buf;
264987da915Sopenharmony_ci	int ret;
265987da915Sopenharmony_ci
266987da915Sopenharmony_ci	ntfs_log_debug("fstrim: start=%llu len=%llu minlen=%llu\n",
267987da915Sopenharmony_ci		(unsigned long long) start,
268987da915Sopenharmony_ci		(unsigned long long) len,
269987da915Sopenharmony_ci		(unsigned long long) minlen);
270987da915Sopenharmony_ci
271987da915Sopenharmony_ci	*trimmed = 0;
272987da915Sopenharmony_ci
273987da915Sopenharmony_ci	/* Fail if user tries to use the fstrim -o/-l/-m options.
274987da915Sopenharmony_ci	 * XXX We could fix these limitations in future.
275987da915Sopenharmony_ci	 */
276987da915Sopenharmony_ci	if (start != 0 || len != (uint64_t)-1) {
277987da915Sopenharmony_ci		ntfs_log_error("fstrim: setting start or length is not supported\n");
278987da915Sopenharmony_ci		return -EINVAL;
279987da915Sopenharmony_ci	}
280987da915Sopenharmony_ci	if (minlen > vol->cluster_size) {
281987da915Sopenharmony_ci		ntfs_log_error("fstrim: minlen > cluster size is not supported\n");
282987da915Sopenharmony_ci		return -EINVAL;
283987da915Sopenharmony_ci	}
284987da915Sopenharmony_ci
285987da915Sopenharmony_ci	/* Only block devices are supported.  It would be possible to
286987da915Sopenharmony_ci	 * support backing files (ie. without using loop) but the
287987da915Sopenharmony_ci	 * ioctls used to punch holes in files are completely
288987da915Sopenharmony_ci	 * different.
289987da915Sopenharmony_ci	 */
290987da915Sopenharmony_ci	if (!NDevBlock(vol->dev)) {
291987da915Sopenharmony_ci		ntfs_log_error("fstrim: not supported for non-block-device\n");
292987da915Sopenharmony_ci		return -EOPNOTSUPP;
293987da915Sopenharmony_ci	}
294987da915Sopenharmony_ci
295987da915Sopenharmony_ci	ret = fstrim_limits(vol, &discard_alignment,
296987da915Sopenharmony_ci			&discard_granularity, &discard_max_bytes);
297987da915Sopenharmony_ci	if (ret)
298987da915Sopenharmony_ci		return ret;
299987da915Sopenharmony_ci	if (discard_alignment != 0) {
300987da915Sopenharmony_ci		ntfs_log_error("fstrim: backing device is not aligned for discards\n");
301987da915Sopenharmony_ci		return -EOPNOTSUPP;
302987da915Sopenharmony_ci	}
303987da915Sopenharmony_ci
304987da915Sopenharmony_ci	if (discard_max_bytes == 0) {
305987da915Sopenharmony_ci		ntfs_log_error("fstrim: backing device does not support discard (discard_max_bytes == 0)\n");
306987da915Sopenharmony_ci		return -EOPNOTSUPP;
307987da915Sopenharmony_ci	}
308987da915Sopenharmony_ci
309987da915Sopenharmony_ci	/* Sync the device before doing anything. */
310987da915Sopenharmony_ci	ret = ntfs_device_sync(vol->dev);
311987da915Sopenharmony_ci	if (ret)
312987da915Sopenharmony_ci		return ret;
313987da915Sopenharmony_ci
314987da915Sopenharmony_ci	/* Read through the bitmap. */
315987da915Sopenharmony_ci	buf = ntfs_malloc(FSTRIM_BUFSIZ);
316987da915Sopenharmony_ci	if (buf == NULL)
317987da915Sopenharmony_ci		return -errno;
318987da915Sopenharmony_ci	for (start_buf = 0; start_buf < vol->nr_clusters;
319987da915Sopenharmony_ci	     start_buf += FSTRIM_BUFSIZ * 8) {
320987da915Sopenharmony_ci		s64 count;
321987da915Sopenharmony_ci		s64 br;
322987da915Sopenharmony_ci		LCN end_buf, start_lcn;
323987da915Sopenharmony_ci
324987da915Sopenharmony_ci		/* start_buf is LCN of first cluster in the current buffer.
325987da915Sopenharmony_ci		 * end_buf is LCN of last cluster + 1 in the current buffer.
326987da915Sopenharmony_ci		 */
327987da915Sopenharmony_ci		end_buf = start_buf + FSTRIM_BUFSIZ*8;
328987da915Sopenharmony_ci		if (end_buf > vol->nr_clusters)
329987da915Sopenharmony_ci			end_buf = vol->nr_clusters;
330987da915Sopenharmony_ci		count = (end_buf - start_buf) / 8;
331987da915Sopenharmony_ci
332987da915Sopenharmony_ci		br = ntfs_attr_pread(vol->lcnbmp_na, start_buf/8, count, buf);
333987da915Sopenharmony_ci		if (br != count) {
334987da915Sopenharmony_ci			if (br >= 0)
335987da915Sopenharmony_ci				ret = -EIO;
336987da915Sopenharmony_ci			else
337987da915Sopenharmony_ci				ret = -errno;
338987da915Sopenharmony_ci			goto free_out;
339987da915Sopenharmony_ci		}
340987da915Sopenharmony_ci
341987da915Sopenharmony_ci		/* Trim the clusters in large as possible blocks, but
342987da915Sopenharmony_ci		 * not larger than discard_max_bytes, and compatible
343987da915Sopenharmony_ci		 * with the supported trim granularity.
344987da915Sopenharmony_ci		 */
345987da915Sopenharmony_ci		for (start_lcn = start_buf; start_lcn < end_buf; ++start_lcn) {
346987da915Sopenharmony_ci			if (!ntfs_bit_get(buf, start_lcn-start_buf)) {
347987da915Sopenharmony_ci				LCN end_lcn;
348987da915Sopenharmony_ci				LCN aligned_lcn;
349987da915Sopenharmony_ci				u64 aligned_count;
350987da915Sopenharmony_ci
351987da915Sopenharmony_ci				/* Cluster 'start_lcn' is not in use,
352987da915Sopenharmony_ci				 * find end of this run.
353987da915Sopenharmony_ci				 */
354987da915Sopenharmony_ci				end_lcn = start_lcn+1;
355987da915Sopenharmony_ci				while (end_lcn < end_buf &&
356987da915Sopenharmony_ci					(u64) (end_lcn-start_lcn) << vol->cluster_size_bits
357987da915Sopenharmony_ci					  < discard_max_bytes &&
358987da915Sopenharmony_ci					!ntfs_bit_get(buf, end_lcn-start_buf))
359987da915Sopenharmony_ci					end_lcn++;
360987da915Sopenharmony_ci				aligned_lcn = align_up(vol, start_lcn,
361987da915Sopenharmony_ci						discard_granularity);
362987da915Sopenharmony_ci				if (aligned_lcn >= end_lcn)
363987da915Sopenharmony_ci					aligned_count = 0;
364987da915Sopenharmony_ci				else {
365987da915Sopenharmony_ci					aligned_count =
366987da915Sopenharmony_ci						align_down(vol,
367987da915Sopenharmony_ci							end_lcn - aligned_lcn,
368987da915Sopenharmony_ci							discard_granularity);
369987da915Sopenharmony_ci				}
370987da915Sopenharmony_ci				if (aligned_count) {
371987da915Sopenharmony_ci					ret = fstrim_clusters(vol,
372987da915Sopenharmony_ci						aligned_lcn, aligned_count);
373987da915Sopenharmony_ci					if (ret)
374987da915Sopenharmony_ci						goto free_out;
375987da915Sopenharmony_ci
376987da915Sopenharmony_ci					*trimmed += aligned_count
377987da915Sopenharmony_ci						<< vol->cluster_size_bits;
378987da915Sopenharmony_ci				}
379987da915Sopenharmony_ci				start_lcn = end_lcn-1;
380987da915Sopenharmony_ci			}
381987da915Sopenharmony_ci		}
382987da915Sopenharmony_ci	}
383987da915Sopenharmony_ci
384987da915Sopenharmony_ci	ret = 0;
385987da915Sopenharmony_cifree_out:
386987da915Sopenharmony_ci	free(buf);
387987da915Sopenharmony_ci	return ret;
388987da915Sopenharmony_ci}
389987da915Sopenharmony_ci
390987da915Sopenharmony_ci#endif /* FITRIM && BLKDISCARD */
391987da915Sopenharmony_ci
392987da915Sopenharmony_ciint ntfs_ioctl(ntfs_inode *ni, unsigned long cmd,
393987da915Sopenharmony_ci			void *arg __attribute__((unused)),
394987da915Sopenharmony_ci			unsigned int flags __attribute__((unused)), void *data)
395987da915Sopenharmony_ci{
396987da915Sopenharmony_ci	int ret = 0;
397987da915Sopenharmony_ci
398987da915Sopenharmony_ci	switch (cmd) {
399987da915Sopenharmony_ci#if defined(FITRIM) && defined(BLKDISCARD)
400987da915Sopenharmony_ci	case FITRIM:
401987da915Sopenharmony_ci		if (!ni || !data)
402987da915Sopenharmony_ci			ret = -EINVAL;
403987da915Sopenharmony_ci		else {
404987da915Sopenharmony_ci			u64 trimmed;
405987da915Sopenharmony_ci			struct fstrim_range *range = (struct fstrim_range*)data;
406987da915Sopenharmony_ci
407987da915Sopenharmony_ci			ret = fstrim(ni->vol, data, &trimmed);
408987da915Sopenharmony_ci			range->len = trimmed;
409987da915Sopenharmony_ci		}
410987da915Sopenharmony_ci		break;
411987da915Sopenharmony_ci#else
412987da915Sopenharmony_ci#warning Trimming not supported : FITRIM or BLKDISCARD not defined
413987da915Sopenharmony_ci#endif
414987da915Sopenharmony_ci	default :
415987da915Sopenharmony_ci		ret = -EINVAL;
416987da915Sopenharmony_ci		break;
417987da915Sopenharmony_ci	}
418987da915Sopenharmony_ci	return (ret);
419987da915Sopenharmony_ci}
420