~ubuntu-branches/debian/sid/ntfs-3g/sid

« back to all changes in this revision

Viewing changes to libntfs-3g/ioctl.c

  • Committer: Package Import Robot
  • Author(s): Laszlo Boszormenyi (GCS)
  • Date: 2014-10-05 09:34:12 UTC
  • mfrom: (1.2.6)
  • Revision ID: package-import@ubuntu.com-20141005093412-4ohtygxt9pm9v5u9
Tags: 1:2014.2.15AR.2-1
* New upstream release.
* Remove 0001-type-inconsistencies.patch and 0002-exit-values.patch , this
  release contains those.
* Update Standards-Version to 3.9.6 .

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**
 
2
 * ioctl.c - Processing of ioctls
 
3
 *
 
4
 *      This module is part of ntfs-3g library
 
5
 *
 
6
 * Copyright (c) 2014 Jean-Pierre Andre
 
7
 * Copyright (c) 2014 Red Hat, Inc.
 
8
 *
 
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.
 
13
 *
 
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.
 
18
 *
 
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
 
23
 */
 
24
 
 
25
#include "config.h"
 
26
 
 
27
#ifdef HAVE_STDIO_H
 
28
#include <stdio.h>
 
29
#endif
 
30
#ifdef HAVE_INTTYPES_H
 
31
#include <inttypes.h>
 
32
#endif
 
33
#ifdef HAVE_STRING_H
 
34
#include <string.h>
 
35
#endif
 
36
#ifdef HAVE_ERRNO_H
 
37
#include <errno.h>
 
38
#endif
 
39
#ifdef HAVE_FCNTL_H
 
40
#include <fcntl.h>
 
41
#endif
 
42
#ifdef HAVE_UNISTD_H
 
43
#include <unistd.h>
 
44
#endif
 
45
#ifdef HAVE_STDLIB_H
 
46
#include <stdlib.h>
 
47
#endif
 
48
#ifdef HAVE_LIMITS_H
 
49
#include <limits.h>
 
50
#endif
 
51
#include <syslog.h>
 
52
 
 
53
#ifdef HAVE_SETXATTR
 
54
#include <sys/xattr.h>
 
55
#endif
 
56
 
 
57
#ifdef HAVE_SYS_TYPES_H
 
58
#include <sys/types.h>
 
59
#endif
 
60
 
 
61
#ifdef HAVE_SYS_STAT_H
 
62
#include <sys/stat.h>
 
63
#endif
 
64
 
 
65
#ifdef HAVE_LINUX_FS_H
 
66
#include <linux/fs.h>
 
67
#endif
 
68
 
 
69
#include "compat.h"
 
70
#include "debug.h"
 
71
#include "bitmap.h"
 
72
#include "attrib.h"
 
73
#include "inode.h"
 
74
#include "layout.h"
 
75
#include "volume.h"
 
76
#include "index.h"
 
77
#include "logging.h"
 
78
#include "ntfstime.h"
 
79
#include "unistr.h"
 
80
#include "dir.h"
 
81
#include "security.h"
 
82
#include "ioctl.h"
 
83
#include "misc.h"
 
84
 
 
85
#if defined(FITRIM) && defined(BLKDISCARD)
 
86
 
 
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)
 
89
{
 
90
        struct ntfs_device *dev = vol->dev;
 
91
        uint64_t range[2];
 
92
 
 
93
        ntfs_log_debug("fstrim_clusters: %lld length %lld\n",
 
94
                (long long) lcn, (long long) length);
 
95
 
 
96
        range[0] = lcn << vol->cluster_size_bits;
 
97
        range[1] = length << vol->cluster_size_bits;
 
98
 
 
99
        if (dev->d_ops->ioctl(dev, BLKDISCARD, range) == -1) {
 
100
                ntfs_log_debug("fstrim_one_cluster: ioctl failed: %m\n");
 
101
                return -errno;
 
102
        }
 
103
        return 0;
 
104
}
 
105
 
 
106
static int read_line(const char *path, char *line, size_t max_bytes)
 
107
{
 
108
        FILE *fp;
 
109
 
 
110
        fp = fopen(path, "r");
 
111
        if (fp == NULL)
 
112
                return -errno;
 
113
        if (fgets(line, max_bytes, fp) == NULL) {
 
114
                int ret = -EIO; /* fgets doesn't set errno */
 
115
                fclose(fp);
 
116
                return ret;
 
117
        }
 
118
        fclose (fp);
 
119
        return 0;
 
120
}
 
121
 
 
122
static int read_u64(const char *path, u64 *n)
 
123
{
 
124
        char line[64];
 
125
        int ret;
 
126
 
 
127
        ret = read_line(path, line, sizeof line);
 
128
        if (ret)
 
129
                return ret;
 
130
        if (sscanf(line, "%" SCNu64, n) != 1)
 
131
                return -EINVAL;
 
132
        return 0;
 
133
}
 
134
 
 
135
/* Find discard limits for current backing device.
 
136
 */
 
137
static int fstrim_limits(ntfs_volume *vol,
 
138
                        u64 *discard_alignment,
 
139
                        u64 *discard_granularity,
 
140
                        u64 *discard_max_bytes)
 
141
{
 
142
        struct stat statbuf;
 
143
        char path1[80], path2[80];
 
144
        int ret;
 
145
 
 
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",
 
149
                        vol->dev->d_name);
 
150
                return -errno;
 
151
        }
 
152
 
 
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
 
157
         * will exist.
 
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
 
161
         */
 
162
        snprintf(path1, sizeof path1, "/sys/dev/block/%d:%d",
 
163
                major(statbuf.st_rdev), minor(statbuf.st_rdev));
 
164
 
 
165
        snprintf(path2, sizeof path2, "%s/discard_alignment", path1);
 
166
        ret = read_u64(path2, discard_alignment);
 
167
        if (ret) {
 
168
                if (ret != -ENOENT)
 
169
                        return ret;
 
170
                else
 
171
                        /* We would expect this file to exist on all
 
172
                         * modern kernels.  But for the sake of very
 
173
                         * old kernels:
 
174
                         */
 
175
                        goto not_found;
 
176
        }
 
177
 
 
178
        snprintf(path2, sizeof path2, "%s/queue/discard_granularity", path1);
 
179
        ret = read_u64(path2, discard_granularity);
 
180
        if (ret) {
 
181
                if (ret != -ENOENT)
 
182
                        return ret;
 
183
                else {
 
184
                        snprintf(path2, sizeof path2,
 
185
                                "%s/../queue/discard_granularity", path1);
 
186
                        ret = read_u64(path2, discard_granularity);
 
187
                        if (ret) {
 
188
                                if (ret != -ENOENT)
 
189
                                        return ret;
 
190
                                else
 
191
                                        goto not_found;
 
192
                        }
 
193
                }
 
194
        }
 
195
 
 
196
        snprintf(path2, sizeof path2, "%s/queue/discard_max_bytes", path1);
 
197
        ret = read_u64(path2, discard_max_bytes);
 
198
        if (ret) {
 
199
                if (ret != -ENOENT)
 
200
                        return ret;
 
201
                else {
 
202
                        snprintf(path2, sizeof path2,
 
203
                                "%s/../queue/discard_max_bytes", path1);
 
204
                        ret = read_u64(path2, discard_max_bytes);
 
205
                        if (ret) {
 
206
                                if (ret != -ENOENT)
 
207
                                        return ret;
 
208
                                else
 
209
                                        goto not_found;
 
210
                        }
 
211
                }
 
212
        }
 
213
 
 
214
        return 0;
 
215
 
 
216
not_found:
 
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.
 
220
         */
 
221
        *discard_alignment = 0;
 
222
        *discard_granularity = 0;
 
223
        *discard_max_bytes = 0;
 
224
        return 0;
 
225
}
 
226
 
 
227
#define FSTRIM_BUFSIZ 4096
 
228
 
 
229
/* Trim the filesystem.
 
230
 *
 
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.
 
234
 */
 
235
static int fstrim(ntfs_volume *vol, void *data)
 
236
{
 
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;
 
242
        u8 *buf = NULL;
 
243
        LCN start_buf;
 
244
        int ret;
 
245
 
 
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);
 
250
 
 
251
        /* Fail if user tries to use the fstrim -o/-l/-m options.
 
252
         * XXX We could fix these limitations in future.
 
253
         */
 
254
        if (start != 0 || len != (uint64_t)-1) {
 
255
                ntfs_log_debug("fstrim: setting start or length is not supported\n");
 
256
                return -EINVAL;
 
257
        }
 
258
        if (minlen > vol->cluster_size) {
 
259
                ntfs_log_debug("fstrim: minlen > cluster size is not supported\n");
 
260
                return -EINVAL;
 
261
        }
 
262
 
 
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
 
266
         * different.
 
267
         */
 
268
        if (!NDevBlock(vol->dev)) {
 
269
                ntfs_log_debug("fstrim: not supported for non-block-device\n");
 
270
                return -EOPNOTSUPP;
 
271
        }
 
272
 
 
273
        ret = fstrim_limits(vol, &discard_alignment,
 
274
                        &discard_granularity, &discard_max_bytes);
 
275
        if (ret)
 
276
                return ret;
 
277
        if (discard_alignment != 0) {
 
278
                ntfs_log_debug("fstrim: backing device is not aligned for discards\n");
 
279
                return -EOPNOTSUPP;
 
280
        }
 
281
        if (discard_granularity > vol->cluster_size) {
 
282
                ntfs_log_debug("fstrim: discard granularity of backing device is larger than cluster size\n");
 
283
                return -EOPNOTSUPP;
 
284
        }
 
285
        if (discard_max_bytes == 0) {
 
286
                ntfs_log_debug("fstrim: backing device does not support discard (discard_max_bytes == 0)\n");
 
287
                return -EOPNOTSUPP;
 
288
        }
 
289
 
 
290
        /* Sync the device before doing anything. */
 
291
        ret = ntfs_device_sync(vol->dev);
 
292
        if (ret)
 
293
                return ret;
 
294
 
 
295
        /* Read through the bitmap. */
 
296
        buf = ntfs_malloc(FSTRIM_BUFSIZ);
 
297
        if (buf == NULL)
 
298
                return -errno;
 
299
        for (start_buf = 0; start_buf < vol->nr_clusters;
 
300
             start_buf += FSTRIM_BUFSIZ * 8) {
 
301
                s64 count;
 
302
                s64 br;
 
303
                LCN end_buf, start_lcn;
 
304
 
 
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.
 
307
                 */
 
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;
 
312
 
 
313
                br = ntfs_attr_pread(vol->lcnbmp_na, start_buf/8, count, buf);
 
314
                if (br != count) {
 
315
                        if (br >= 0)
 
316
                                ret = -EIO;
 
317
                        else
 
318
                                ret = -errno;
 
319
                        goto free_out;
 
320
                }
 
321
 
 
322
                /* Trim the clusters in large as possible blocks, but
 
323
                 * not larger than discard_max_bytes.
 
324
                 */
 
325
                for (start_lcn = start_buf; start_lcn < end_buf; ++start_lcn) {
 
326
                        if (!ntfs_bit_get(buf, start_lcn-start_buf)) {
 
327
                                LCN end_lcn;
 
328
 
 
329
                                /* Cluster 'start_lcn' is not in use,
 
330
                                 * find end of this run.
 
331
                                 */
 
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))
 
337
                                        end_lcn++;
 
338
 
 
339
                                ret = fstrim_clusters(vol,
 
340
                                                start_lcn, end_lcn-start_lcn);
 
341
                                if (ret)
 
342
                                        goto free_out;
 
343
 
 
344
                                start_lcn = end_lcn-1;
 
345
                        }
 
346
                }
 
347
        }
 
348
 
 
349
        ret = 0;
 
350
free_out:
 
351
        free(buf);
 
352
        return ret;
 
353
}
 
354
 
 
355
#endif /* FITRIM && BLKDISCARD */
 
356
 
 
357
int ntfs_ioctl(ntfs_inode *ni, int cmd, void *arg __attribute__((unused)),
 
358
                        unsigned int flags __attribute__((unused)), void *data)
 
359
{
 
360
        int ret = 0;
 
361
 
 
362
        switch (cmd) {
 
363
#if defined(FITRIM) && defined(BLKDISCARD)
 
364
        case FITRIM:
 
365
                if (!ni || !data)
 
366
                        ret = -EINVAL;
 
367
                else
 
368
                        ret = fstrim(ni->vol, data);
 
369
                break;
 
370
#else
 
371
#warning FITRIM or BLKDISCARD not defined
 
372
#endif
 
373
        default :
 
374
                ret = -EINVAL;
 
375
                break;
 
376
        }
 
377
        return (ret);
 
378
}