"Fossies" - the Fresh Open Source Software Archive

Member "ntfs-3g_ntfsprogs-2017.3.23/libntfs-3g/ioctl.c" (23 Mar 2017, 9548 Bytes) of package /linux/misc/ntfs-3g_ntfsprogs-2017.3.23.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ioctl.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3g_ntfsprogs-2016.2.22_vs_3g_ntfsprogs-2017.3.23.

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