Home | History | Annotate | Line # | Download | only in mount
      1 /*	$NetBSD: mount_linux.c,v 1.1.1.3 2015/01/17 16:34:16 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1997-2014 Erez Zadok
      5  * Copyright (c) 1990 Jan-Simon Pendry
      6  * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
      7  * Copyright (c) 1990 The Regents of the University of California.
      8  * All rights reserved.
      9  *
     10  * This code is derived from software contributed to Berkeley by
     11  * Jan-Simon Pendry at Imperial College, London.
     12  *
     13  * Redistribution and use in source and binary forms, with or without
     14  * modification, are permitted provided that the following conditions
     15  * are met:
     16  * 1. Redistributions of source code must retain the above copyright
     17  *    notice, this list of conditions and the following disclaimer.
     18  * 2. Redistributions in binary form must reproduce the above copyright
     19  *    notice, this list of conditions and the following disclaimer in the
     20  *    documentation and/or other materials provided with the distribution.
     21  * 3. Neither the name of the University nor the names of its contributors
     22  *    may be used to endorse or promote products derived from this software
     23  *    without specific prior written permission.
     24  *
     25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     35  * SUCH DAMAGE.
     36  *
     37  *
     38  * File: am-utils/conf/mount/mount_linux.c
     39  */
     40 
     41 /*
     42  * Linux mount helper.
     43  */
     44 
     45 #ifdef HAVE_CONFIG_H
     46 # include <config.h>
     47 #endif /* HAVE_CONFIG_H */
     48 #include <am_defs.h>
     49 #include <amu.h>
     50 #include <nfs_common.h>
     51 
     52 #ifdef HAVE_RPC_AUTH_H
     53 # include <rpc/auth.h>
     54 #endif
     55 
     56 #ifndef MOUNT_TYPE_UFS
     57 /*
     58  * Autoconf didn't find any disk-based f/s on this system,
     59  * So provide some default definition for this file to compile.
     60  */
     61 # define MOUNT_TYPE_UFS		"no_disk_fs"
     62 #endif /* not MOUNT_TYPE_UFS */
     63 
     64 struct opt_map {
     65   const char *opt;		/* option name */
     66   int inv;			/* true if flag value should be inverted */
     67   int mask;			/* flag mask value */
     68 };
     69 
     70 const struct opt_map opt_map[] =
     71 {
     72   {"defaults",		0,	0},
     73   {MNTTAB_OPT_RO,	0,	MNT2_GEN_OPT_RDONLY},
     74   {MNTTAB_OPT_RW,	1,	MNT2_GEN_OPT_RDONLY},
     75   {MNTTAB_OPT_EXEC,	1,	MNT2_GEN_OPT_NOEXEC},
     76   {MNTTAB_OPT_NOEXEC,	0,	MNT2_GEN_OPT_NOEXEC},
     77   {MNTTAB_OPT_SUID,	1,	MNT2_GEN_OPT_NOSUID},
     78   {MNTTAB_OPT_NOSUID,	0,	MNT2_GEN_OPT_NOSUID},
     79 #ifdef MNT2_GEN_OPT_NODEV
     80   {MNTTAB_OPT_NODEV,	0,	MNT2_GEN_OPT_NODEV},
     81 #endif /* MNT2_GEN_OPT_NODEV */
     82 #ifdef MNT2_GEN_OPT_SYNC
     83   {MNTTAB_OPT_SYNC,	0,	MNT2_GEN_OPT_SYNC},
     84   {MNTTAB_OPT_ASYNC,	1,	MNT2_GEN_OPT_SYNC},
     85 #endif /* MNT2_GEN_OPT_SYNC */
     86 #ifdef MNT2_GEN_OPT_NOSUB
     87   {MNTTAB_OPT_SUB,	1,	MNT2_GEN_OPT_NOSUB},
     88   {MNTTAB_OPT_NOSUB,	0,	MNT2_GEN_OPT_NOSUB},
     89 #endif /* MNT2_GEN_OPT_NOSUB */
     90 #ifdef MNT2_GEN_OPT_SYNCHRONOUS
     91   {"synchronous",	0,	MNT2_GEN_OPT_SYNCHRONOUS},
     92 #endif /* MNT2_GEN_OPT_SYNCHRONOUS */
     93 #ifdef MNT2_GEN_OPT_MANDLOCK
     94   {"mandlock",		0,	MNT2_GEN_OPT_MANDLOCK},
     95 #endif /* MNT2_GEN_OPT_MANDLOCK */
     96 #ifdef MNT2_GEN_OPT_NOATIME
     97   {"noatime",		0,	MNT2_GEN_OPT_NOATIME},
     98 #endif /* MNT2_GEN_OPT_NOATIME */
     99 #ifdef MNT2_GEN_OPT_NODIRATIME
    100   {"nodiratime",	0,	MNT2_GEN_OPT_NODIRATIME},
    101 #endif /* MNT2_GEN_OPT_NODIRATIME */
    102   {NULL,		0,	0}
    103 };
    104 
    105 struct fs_opts {
    106   const char *opt;
    107   int type;			/* XXX: Ion, what is this for? */
    108 };
    109 
    110 const struct fs_opts iso_opts[] = {
    111   { "map",	0 },
    112   { "norock",	0 },
    113   { "cruft",	0 },
    114   { "unhide",	0 },
    115   { "conv",	1 },
    116   { "block",	1 },
    117   { "mode",	1 },
    118   { "gid",	1 },
    119   { "uid",	1 },
    120   { NULL,	0 }
    121 };
    122 
    123 const struct fs_opts dos_opts[] = {
    124   { "check",	1 },
    125   { "conv",	1 },
    126   { "uid",	1 },
    127   { "gid",	1 },
    128   { "umask",	1 },
    129   { "debug",	0 },
    130   { "fat",	1 },
    131   { "quiet",	0 },
    132   { "blocksize",1 },
    133   { NULL,	0 }
    134 };
    135 
    136 const struct fs_opts autofs_opts[] = {
    137   { "fd",	1 },
    138   { "pgrp",	1 },
    139   { "minproto",	1 },
    140   { "maxproto",	1 },
    141   { NULL,	0 }
    142 };
    143 
    144 const struct fs_opts lustre_opts[] = {
    145   { "flock",		0 },
    146   { "localflock",	0 },
    147   { NULL,		0 }
    148 };
    149 
    150 const struct fs_opts null_opts[] = {
    151   { NULL,	0 }
    152 };
    153 
    154 const struct fs_opts ext2_opts[] = {
    155   { "check",			1 },
    156   { "nocheck",			0 },
    157   { "debug",			0 },
    158   { "errors",			1 },
    159   { "grpid",			0 },
    160   { "nogrpid",			0 },
    161   { "bsdgroups",		0 },
    162   { "sysvgroups",		0 },
    163   { "grpquota",			0 },
    164   { "usrquota",			0 },
    165   { "noquota",			0 },
    166   { "quota",			0 },
    167   { "nouid32",			0 },
    168   { "oldalloc",			0 },
    169   { "orlov",			0 },
    170   { "resgid",			1 },
    171   { "resuid",			1 },
    172   { "sb",			1 },
    173   { "user_xattr",		1 },
    174   { "nouser_xattr",		1 },
    175   { "journal_dev",		0 },
    176   { "norecovery",		0 },
    177   { "noload",			0 },
    178   { "data",			1 },
    179   { "barrier",			1 },
    180   { "commit",			1 },
    181   { "user_xattr",		0 },
    182   { "nouser_xattr",		0 },
    183   { "acl",			0 },
    184   { "noacl",			0 },
    185   { "bsddf",			0 },
    186   { "minixdf",			0 },
    187   { "usrjquota",		1 },
    188   { "grpjquota",		1 },
    189   { "jqfmt",			1 },
    190   { NULL,	0 }
    191 };
    192 
    193 const struct fs_opts ext3_opts[] = {
    194   { "check",			1 },
    195   { "nocheck",			0 },
    196   { "debug",			0 },
    197   { "errors",			1 },
    198   { "grpid",			0 },
    199   { "nogrpid",			0 },
    200   { "bsdgroups",		0 },
    201   { "sysvgroups",		0 },
    202   { "grpquota",			0 },
    203   { "usrquota",			0 },
    204   { "noquota",			0 },
    205   { "quota",			0 },
    206   { "nouid32",			0 },
    207   { "oldalloc",			0 },
    208   { "orlov",			0 },
    209   { "resgid",			1 },
    210   { "resuid",			1 },
    211   { "sb",			1 },
    212   { "user_xattr",		1 },
    213   { "nouser_xattr",		1 },
    214   { "journal",			1 },
    215   { "journal_dev",		1 },
    216   { "norecovery",		0 },
    217   { "noload",			0 },
    218   { "data",			1 },
    219   { "barrier",			1 },
    220   { "commit",			1 },
    221   { "user_xattr",		0 },
    222   { "nouser_xattr",		0 },
    223   { "acl",			0 },
    224   { "noacl",			0 },
    225   { "bsddf",			0 },
    226   { "minixdf",			0 },
    227   { "usrjquota",		1 },
    228   { "grpjquota",		1 },
    229   { "jqfmt",			1 },
    230   { NULL,	0 }
    231 };
    232 
    233 const struct fs_opts ext4_opts[] = {
    234   { "debug",			0 },
    235   { "errors",			1 },
    236   { "grpid",			0 },
    237   { "nogrpid",			0 },
    238   { "bsdgroups",		0 },
    239   { "sysvgroups",		0 },
    240   { "grpquota",			0 },
    241   { "usrquota",			0 },
    242   { "noquota",			0 },
    243   { "quota",			0 },
    244   { "oldalloc",			0 },
    245   { "orlov",			0 },
    246   { "resgid",			1 },
    247   { "resuid",			1 },
    248   { "sb",			1 },
    249   { "user_xattr",		1 },
    250   { "nouser_xattr",		1 },
    251   { "journal",			1 },
    252   { "journal_dev",		1 },
    253   { "noload",			0 },
    254   { "data",			1 },
    255   { "commit",			1 },
    256   { "user_xattr",		0 },
    257   { "nouser_xattr",		0 },
    258   { "acl",			0 },
    259   { "noacl",			0 },
    260   { "bsddf",			0 },
    261   { "minixdf",			0 },
    262   { "usrjquota",		1 },
    263   { "grpjquota",		1 },
    264   { "jqfmt",			1 },
    265   { "journal_checksum",		0 },
    266   { "journal_async_commit",	0 },
    267   { "journal",			1 },
    268   { "barrier",			1 },
    269   { "nobarrier",		0 },
    270   { "inode_readahead_blks",	1 },
    271   { "stripe",			1 },
    272   { "delalloc",			0 },
    273   { "nodelalloc",		0 },
    274   { "min_batch_time",		1 },
    275   { "mxn_batch_time",		1 },
    276   { "journal_ioprio",		1 },
    277   { "abort",			0 },
    278   { "auto_da_alloc",		0 },
    279   { "noauto_da_alloc",		0 },
    280   { "discard",			0 },
    281   { "nodiscard",		0 },
    282   { "nouid32",			0 },
    283   { "resize",			0 },
    284   { "block_validity",		0 },
    285   { "noblock_validity",		0 },
    286   { "dioread_lock",		0 },
    287   { "dioread_nolock",		0 },
    288   { NULL,	0 }
    289 };
    290 
    291 
    292 /*
    293  * New parser for linux-specific mounts.
    294  * Should now handle fs-type specific mount-options correctly.
    295  * Currently implemented: msdos, iso9660.
    296  */
    297 static char *
    298 parse_opts(char *type, const char *optstr, int *flags, char **xopts, int *noauto)
    299 {
    300   const struct opt_map *std_opts;
    301   const struct fs_opts *dev_opts;
    302   char *opt, *topts, *xoptstr;
    303   size_t l;
    304 
    305   if (optstr == NULL)
    306     return NULL;
    307 
    308   xoptstr = xstrdup(optstr);	/* because strtok is destructive below */
    309 
    310   *noauto = 0;
    311   l = strlen(optstr) + 2;
    312   *xopts = (char *) xmalloc(l);
    313   topts = (char *) xmalloc(l);
    314   *topts = '\0';
    315   **xopts = '\0';
    316 
    317   for (opt = strtok(xoptstr, ","); opt; opt = strtok(NULL, ",")) {
    318     /*
    319      * First, parse standard options
    320      */
    321     std_opts = opt_map;
    322     while (std_opts->opt &&
    323 	   !NSTREQ(std_opts->opt, opt, strlen(std_opts->opt)))
    324       ++std_opts;
    325     if (!(*noauto = STREQ(opt, MNTTAB_OPT_NOAUTO)) || std_opts->opt) {
    326       xstrlcat(topts, opt, l);
    327       xstrlcat(topts, ",", l);
    328       if (std_opts->inv)
    329 	*flags &= ~std_opts->mask;
    330       else
    331 	*flags |= std_opts->mask;
    332     }
    333     /*
    334      * Next, select which fs-type is to be used
    335      * and parse the fs-specific options
    336      */
    337 #ifdef MOUNT_TYPE_AUTOFS
    338     if (STREQ(type, MOUNT_TYPE_AUTOFS)) {
    339       dev_opts = autofs_opts;
    340       goto do_opts;
    341     }
    342 #endif /* MOUNT_TYPE_AUTOFS */
    343 #ifdef MOUNT_TYPE_PCFS
    344     if (STREQ(type, MOUNT_TYPE_PCFS)) {
    345       dev_opts = dos_opts;
    346       goto do_opts;
    347     }
    348 #endif /* MOUNT_TYPE_PCFS */
    349 #ifdef MOUNT_TYPE_CDFS
    350     if (STREQ(type, MOUNT_TYPE_CDFS)) {
    351       dev_opts = iso_opts;
    352       goto do_opts;
    353     }
    354 #endif /* MOUNT_TYPE_CDFS */
    355 #ifdef MOUNT_TYPE_LOFS
    356     if (STREQ(type, MOUNT_TYPE_LOFS)) {
    357       dev_opts = null_opts;
    358       goto do_opts;
    359     }
    360 #endif /* MOUNT_TYPE_LOFS */
    361 #ifdef MOUNT_TYPE_LUSTRE
    362     if (STREQ(type, MOUNT_TYPE_LUSTRE)) {
    363       dev_opts = lustre_opts;
    364       goto do_opts;
    365     }
    366 #endif /* MOUNT_TYPE_LUSTRE */
    367 #ifdef MOUNT_TYPE_EXT2
    368     if (STREQ(type, MOUNT_TYPE_EXT2)) {
    369       dev_opts = ext2_opts;
    370       goto do_opts;
    371     }
    372 #endif /* MOUNT_TYPE_EXT2 */
    373 #ifdef MOUNT_TYPE_EXT3
    374     if (STREQ(type, MOUNT_TYPE_EXT3)) {
    375       dev_opts = ext3_opts;
    376       goto do_opts;
    377     }
    378 #endif /* MOUNT_TYPE_EXT3 */
    379 #ifdef MOUNT_TYPE_EXT4
    380     if (STREQ(type, MOUNT_TYPE_EXT4)) {
    381       dev_opts = ext4_opts;
    382       goto do_opts;
    383     }
    384 #endif /* MOUNT_TYPE_EXT4 */
    385     plog(XLOG_FATAL, "linux mount: unknown fs-type: %s\n", type);
    386     XFREE(xoptstr);
    387     XFREE(*xopts);
    388     XFREE(topts);
    389     return NULL;
    390 
    391 do_opts:
    392     while (dev_opts->opt &&
    393 	   (!NSTREQ(dev_opts->opt, opt, strlen(dev_opts->opt)))) {
    394       ++dev_opts;
    395     }
    396     if (dev_opts->opt) {
    397       xstrlcat(*xopts, opt, l);
    398       xstrlcat(*xopts, ",", l);
    399     }
    400   }
    401   /*
    402    * All other options are discarded
    403    */
    404   if (strlen(*xopts))
    405     *(*xopts + strlen(*xopts)-1) = '\0';
    406   if (strlen(topts))
    407     topts[strlen(topts)-1] = '\0';
    408   XFREE(xoptstr);
    409   return topts;
    410 }
    411 
    412 
    413 /*
    414  * Returns combined linux kernel version number.  For a kernel numbered
    415  * x.y.z, returns x*65535+y*256+z.
    416  */
    417 int
    418 linux_version_code(void)
    419 {
    420   char *token;
    421   int shift = 16;
    422   struct utsname my_utsname;
    423   static int release = 0;
    424 
    425   if ( release || uname(&my_utsname))
    426     return release;
    427 
    428   for (token = strtok(my_utsname.release, "."); token && (shift > -1); token = strtok(NULL, "."))
    429   {
    430      release |= (atoi(token) << shift);
    431      shift -= 8;
    432   }
    433 
    434   return release;
    435 }
    436 
    437 
    438 int
    439 do_mount_linux(MTYPE_TYPE type, mntent_t *mnt, int flags, caddr_t data)
    440 {
    441   if (amuDebug(D_FULL)) {
    442     plog(XLOG_DEBUG, "do_mount_linux: fsname %s\n", mnt->mnt_fsname);
    443     plog(XLOG_DEBUG, "do_mount_linux: type (mntent) %s\n", mnt->mnt_type);
    444     plog(XLOG_DEBUG, "do_mount_linux: opts %s\n", mnt->mnt_opts);
    445     plog(XLOG_DEBUG, "do_mount_linux: dir %s\n", mnt->mnt_dir);
    446   }
    447 
    448   /*
    449    * If we have an nfs mount, the 5th argument to system mount() must be the
    450    * nfs_mount_data structure, otherwise it is the return from parse_opts()
    451    */
    452   return mount(mnt->mnt_fsname,
    453 	       mnt->mnt_dir,
    454 	       type,
    455 	       MS_MGC_VAL | flags,
    456 	       data);
    457 }
    458 
    459 static void
    460 setup_nfs_args(struct nfs_common_args *ca)
    461 {
    462   if (!ca->timeo) {
    463 #ifdef MNT2_NFS_OPT_TCP
    464     if (ca->flags & MNT2_NFS_OPT_TCP)
    465       ca->timeo = 600;
    466     else
    467 #endif /* MNT2_NFS_OPT_TCP */
    468       ca->timeo = 7;
    469   }
    470   if (!ca->retrans)
    471     ca->retrans = 3;
    472 
    473 #ifdef MNT2_NFS_OPT_NOAC
    474   if (!(ca->flags & MNT2_NFS_OPT_NOAC)) {
    475     if (!(ca->flags & MNT2_NFS_OPT_ACREGMIN))
    476       ca->acregmin = 3;
    477     if (!(ca->flags & MNT2_NFS_OPT_ACREGMAX))
    478       ca->acregmax = 60;
    479     if (!(ca->flags & MNT2_NFS_OPT_ACDIRMIN))
    480       ca->acdirmin = 30;
    481     if (!(ca->flags & MNT2_NFS_OPT_ACDIRMAX))
    482       ca->acdirmax = 60;
    483   }
    484 #endif /* MNT2_NFS_OPT_NOAC */
    485 }
    486 
    487 
    488 int
    489 mount_linux_nfs(MTYPE_TYPE type, mntent_t *mnt, int flags, caddr_t data)
    490 {
    491   nfs_args_t *mnt_data = (nfs_args_t *) data;
    492   int errorcode;
    493   struct nfs_common_args a;
    494 
    495   /* Fake some values for linux */
    496   mnt_data->version = NFS_MOUNT_VERSION;
    497 
    498   put_nfs_common_args(mnt_data, a);
    499   setup_nfs_args(&a);
    500   get_nfs_common_args(mnt_data, a);
    501 
    502   /*
    503    * in nfs structure implementation version 4, the old
    504    * filehandle field was renamed "old_root" and left as 3rd field,
    505    * while a new field called "root" was added to the end of the
    506    * structure. Both of them however need a copy of the file handle
    507    * for NFSv2 mounts.
    508    */
    509 #ifdef MNT2_NFS_OPT_VER3
    510   if (mnt_data->flags & MNT2_NFS_OPT_VER3)
    511     memset(mnt_data->old_root.data, 0, FHSIZE);
    512   else
    513 #endif /* MNT2_NFS_OPT_VER3 */
    514     memcpy(mnt_data->old_root.data, mnt_data->root.data, FHSIZE);
    515 
    516 #ifdef HAVE_NFS_ARGS_T_BSIZE
    517   /* linux mount version 3 */
    518   mnt_data->bsize = 0;			/* let the kernel decide */
    519 #endif /* HAVE_NFS_ARGS_T_BSIZE */
    520 
    521 #ifdef HAVE_NFS_ARGS_T_NAMLEN
    522   /* linux mount version 2 */
    523   mnt_data->namlen = NAME_MAX;		/* 256 bytes */
    524 #endif /* HAVE_NFS_ARGS_T_NAMELEN */
    525 
    526 #ifdef HAVE_NFS_ARGS_T_PSEUDOFLAVOR
    527 # ifdef HAVE_RPC_AUTH_H
    528   mnt_data->pseudoflavor = AUTH_UNIX;
    529 # else
    530   mnt_data->pseudoflavor = 0;
    531 # endif
    532 #endif /* HAVE_NFS_ARGS_T_PSEUDOFLAVOR */
    533 
    534 #ifdef HAVE_NFS_ARGS_T_CONTEXT
    535   memset(mnt_data->context, 0, sizeof(mnt_data->context));
    536 #endif /* HAVE_NFS_ARGS_T_CONTEXT */
    537 
    538   mnt_data->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    539   if (mnt_data->fd < 0) {
    540     plog(XLOG_ERROR, "Can't create socket for kernel");
    541     return 1;
    542   }
    543   if (bindresvport(mnt_data->fd, NULL) < 0) {
    544     plog(XLOG_ERROR, "Can't bind to reserved port");
    545     errorcode = 1;
    546     goto out;
    547   }
    548   /*
    549    * connect() the socket for kernels 1.3.10 and below
    550    * only to avoid problems with multihomed hosts.
    551    */
    552   if (linux_version_code() <= 0x01030a) {
    553     int ret = connect(mnt_data->fd,
    554 		      (struct sockaddr *) &mnt_data->addr,
    555 		      sizeof(mnt_data->addr));
    556     if (ret < 0) {
    557       plog(XLOG_ERROR, "Can't connect socket for kernel");
    558       errorcode = 1;
    559       goto out;
    560     }
    561   }
    562   if (amuDebug(D_FULL)) {
    563     plog(XLOG_DEBUG, "%s: type %s\n", __func__, type);
    564     plog(XLOG_DEBUG, "%s: version %d\n", __func__, mnt_data->version);
    565     plog(XLOG_DEBUG, "%s: fd %d\n", __func__, mnt_data->fd);
    566     plog(XLOG_DEBUG, "%s: hostname %s\n", __func__,
    567 	 inet_ntoa(mnt_data->addr.sin_addr));
    568     plog(XLOG_DEBUG, "%s: port %d\n", __func__,
    569 	 htons(mnt_data->addr.sin_port));
    570   }
    571   if (amuDebug(D_TRACE)) {
    572     plog(XLOG_DEBUG, "%s: Generic mount flags 0x%x", __func__,
    573 	 MS_MGC_VAL | flags);
    574     plog(XLOG_DEBUG, "%s: updated nfs_args...", __func__);
    575     print_nfs_args(mnt_data, 0);
    576   }
    577 
    578   mnt_data->flags &= MNT2_NFS_OPT_FLAGMASK;
    579 
    580   errorcode = do_mount_linux(type, mnt, flags, data);
    581 
    582  out:
    583   /*
    584    * If we failed, (i.e. errorcode != 0), then close the socket
    585    * if it is open.
    586    */
    587   if (errorcode && mnt_data->fd != -1) {
    588     /* save errno, may be clobbered by close() call! */
    589     int save_errno = errno;
    590     close(mnt_data->fd);
    591     errno = save_errno;
    592   }
    593   return errorcode;
    594 }
    595 
    596 #ifdef HAVE_FS_NFS4
    597 int
    598 mount_linux_nfs4(MTYPE_TYPE type, mntent_t *mnt, int flags, caddr_t data)
    599 {
    600   nfs4_args_t *mnt_data = (nfs4_args_t *) data;
    601   int errorcode;
    602   struct nfs_common_args a;
    603 
    604   /* Fake some values for linux */
    605   mnt_data->version = NFS4_MOUNT_VERSION;
    606 
    607   put_nfs_common_args(mnt_data, a);
    608   setup_nfs_args(&a);
    609   get_nfs_common_args(mnt_data, a);
    610 
    611   if (amuDebug(D_FULL)) {
    612     plog(XLOG_DEBUG, "%s: type %s\n", __func__, type);
    613     plog(XLOG_DEBUG, "%s: version %d\n", __func__, mnt_data->version);
    614   }
    615   if (amuDebug(D_TRACE)) {
    616     plog(XLOG_DEBUG, "%s: Generic mount flags 0x%x", __func__,
    617 	 MS_MGC_VAL | flags);
    618     plog(XLOG_DEBUG, "%s: updated nfs_args...", __func__);
    619     print_nfs_args(mnt_data, NFS_VERSION4);
    620   }
    621 
    622   errorcode = do_mount_linux(type, mnt, flags, data);
    623 
    624   return errorcode;
    625 }
    626 #endif
    627 
    628 int
    629 mount_linux_nonfs(MTYPE_TYPE type, mntent_t *mnt, int flags, caddr_t data)
    630 {
    631   char *extra_opts = NULL;
    632   char *tmp_opts = NULL;
    633   char *sub_type = NULL;
    634   char *loopdev = NULL;
    635   int noauto = 0;
    636   int errorcode;
    637 
    638   sub_type = hasmnteq(mnt, "type");
    639   if (sub_type) {
    640     sub_type = xstrdup(sub_type);
    641     type = strpbrk(sub_type, ",:;\n\t");
    642     if (type == NULL)
    643       type = MOUNT_TYPE_UFS;
    644     else {
    645       *type = '\0';
    646       type = sub_type;
    647     }
    648   }
    649 
    650   if (!hasmntopt(mnt, "type"))
    651     mnt->mnt_type = type;
    652 
    653   tmp_opts = parse_opts(type, mnt->mnt_opts, &flags, &extra_opts, &noauto);
    654 
    655 #ifdef MOUNT_TYPE_LOFS
    656   if (STREQ(type, MOUNT_TYPE_LOFS)) {
    657 # ifndef MNT2_GEN_OPT_BIND
    658     size_t l;
    659     /* this is basically a hack to support fist lofs */
    660     XFREE(extra_opts);
    661     l = strlen(mnt->mnt_fsname) + sizeof("dir=") + 1;
    662     extra_opts = (char *) xmalloc(l);
    663     xsnprintf(extra_opts, l, sizeof(extra_opts), "dir=%s", mnt->mnt_fsname);
    664 # else /* MNT2_GEN_OPT_BIND */
    665     /* use bind mounts for lofs */
    666     flags |= MNT2_GEN_OPT_BIND;
    667 # endif /* MNT2_GEN_OPT_BIND */
    668     errorcode = do_mount_linux(type, mnt, flags, extra_opts);
    669   } else /* end of "if type is LOFS" */
    670 #endif /* MOUNT_TYPE_LOFS */
    671 
    672 #ifdef MOUNT_TYPE_LUSTRE
    673   if (STREQ(type, MOUNT_TYPE_LUSTRE)) {
    674     char *topts;
    675     if (*extra_opts)
    676       topts = strvcat(extra_opts, ",device=", mnt->mnt_fsname, NULL);
    677     else
    678       topts = strvcat("device=", mnt->mnt_fsname, NULL);
    679     free(extra_opts);
    680     extra_opts = topts;
    681   }
    682 #endif /* MOUNT_TYPE_LOFS */
    683 
    684   {
    685 #ifdef HAVE_LOOP_DEVICE
    686     /*
    687      * If the mounted "device" is actually a regular file,
    688      # try to attach a loop device to it.
    689      */
    690     struct stat buf;
    691     char *old_fsname = NULL;
    692     if (stat(mnt->mnt_fsname, &buf) == 0 &&
    693 	S_ISREG(buf.st_mode)) {
    694       if ((loopdev = setup_loop_device(mnt->mnt_fsname)) != NULL) {
    695 	char *str;
    696 	size_t l;
    697 
    698 	plog(XLOG_INFO, "setup loop device %s over %s OK", loopdev, mnt->mnt_fsname);
    699 	old_fsname = mnt->mnt_fsname;
    700 	mnt->mnt_fsname = loopdev;
    701 	/* XXX: hack, append loop=/dev/loopX to mnttab opts */
    702 	l = strlen(mnt->mnt_opts) + 7 + strlen(loopdev);
    703 	str = (char *) xmalloc(l);
    704 	if (str) {
    705 	  xsnprintf(str, l, "%s,loop=%s", mnt->mnt_opts, loopdev);
    706 	  XFREE(mnt->mnt_opts);
    707 	  mnt->mnt_opts = str;
    708 	}
    709       } else {
    710 	plog(XLOG_ERROR, "failed to set up a loop device: %m");
    711 	errorcode = 1;
    712 	goto out;
    713       }
    714     }
    715 #endif /* HAVE_LOOP_DEVICE */
    716 
    717     errorcode = do_mount_linux(type, mnt, flags, extra_opts);
    718 
    719 #ifdef HAVE_LOOP_DEVICE
    720     /* if mount failed and we used a loop device, then undo it */
    721     if (errorcode != 0 && loopdev != NULL) {
    722       if (delete_loop_device(loopdev) < 0)
    723 	plog(XLOG_WARNING, "mount() failed to release loop device %s: %m", loopdev);
    724       else
    725 	plog(XLOG_INFO, "mount() released loop device %s OK", loopdev);
    726     }
    727     if (old_fsname)
    728       mnt->mnt_fsname = old_fsname;
    729 #endif /* HAVE_LOOP_DEVICE */
    730   }
    731 
    732   /*
    733    * Free all allocated space and return errorcode.
    734    */
    735 out:
    736 if (loopdev)
    737      XFREE(loopdev);
    738   if (extra_opts != NULL)
    739     XFREE(extra_opts);
    740   if (tmp_opts != NULL)
    741     XFREE(tmp_opts);
    742   if (sub_type != NULL)
    743     XFREE(sub_type);
    744   return errorcode;
    745 }
    746 
    747 
    748 int
    749 mount_linux(MTYPE_TYPE type, mntent_t *mnt, int flags, caddr_t data)
    750 {
    751   int errorcode;
    752 
    753   if (mnt->mnt_opts && STREQ(mnt->mnt_opts, "defaults"))
    754     mnt->mnt_opts = NULL;
    755 
    756   if (type == NULL)
    757     type = index(mnt->mnt_fsname, ':') ? MOUNT_TYPE_NFS : MOUNT_TYPE_UFS;
    758 
    759 #ifdef HAVE_FS_NFS4
    760   if (STREQ(type, MOUNT_TYPE_NFS4))
    761     errorcode = mount_linux_nfs4(type, mnt, flags, data);
    762   else
    763 #endif
    764   if (STREQ(type, MOUNT_TYPE_NFS))
    765     errorcode = mount_linux_nfs(type, mnt, flags, data);
    766   else				/* non-NFS mounts */
    767     errorcode = mount_linux_nonfs(type, mnt, flags, data);
    768 
    769   return errorcode;
    770 }
    771 
    772 
    773 /****************************************************************************/
    774 /*
    775  * NFS error numbers and Linux errno's are two different things!  Linux is
    776  * `worse' than other OSes in the respect that it loudly complains about
    777  * undefined NFS return value ("bad NFS return value..").  So we should
    778  * translate ANY possible Linux errno to their NFS equivalent.  Just, there
    779  * aren't much NFS numbers, so most go to EINVAL or EIO.  The mapping below
    780  * should fit at least for Linux/i386 and Linux/68k.  I haven't checked
    781  * other architectures yet.
    782  */
    783 
    784 #define NE_PERM		1
    785 #define NE_NOENT	2
    786 #define NE_IO		5
    787 #define NE_NXIO		6
    788 #define NE_AGAIN	11
    789 #define NE_ACCES	13
    790 #define NE_EXIST	17
    791 #define NE_NODEV	19
    792 #define NE_NOTDIR	20
    793 #define NE_ISDIR	21
    794 #define NE_INVAL	22
    795 #define NE_FBIG		27
    796 #define NE_NOSPC	28
    797 #define NE_ROFS		30
    798 #define NE_OPNOTSUPP	45
    799 #define NE_NAMETOOLONG	63
    800 #define NE_NOTEMPTY	66
    801 #define NE_DQUOT	69
    802 #define NE_STALE	70
    803 #define NE_REMOTE	71
    804 
    805 #define NFS_LOMAP	0
    806 #define NFS_HIMAP	122
    807 
    808 /*
    809  * The errno's below are correct for Linux/i386. One day, somebody
    810  * with lots of energy ought to verify them against the other ports...
    811  */
    812 static int nfs_errormap[] = {
    813 	0,		/* success(0)		*/
    814 	NE_PERM,	/* EPERM (1)		*/
    815 	NE_NOENT,	/* ENOENT (2)		*/
    816 	NE_INVAL,	/* ESRCH (3)		*/
    817 	NE_IO,		/* EINTR (4)		*/
    818 	NE_IO,		/* EIO (5)		*/
    819 	NE_NXIO,	/* ENXIO (6)		*/
    820 	NE_INVAL,	/* E2BIG (7)		*/
    821 	NE_INVAL,	/* ENOEXEC (8)		*/
    822 	NE_INVAL,	/* EBADF (9)		*/
    823 	NE_IO,		/* ECHILD (10)		*/
    824 	NE_AGAIN,	/* EAGAIN (11)		*/
    825 	NE_IO,		/* ENOMEM (12)		*/
    826 	NE_ACCES,	/* EACCES (13)		*/
    827 	NE_INVAL,	/* EFAULT (14)		*/
    828 	NE_INVAL,	/* ENOTBLK (15)		*/
    829 	NE_IO,		/* EBUSY (16)		*/
    830 	NE_EXIST,	/* EEXIST (17)		*/
    831 	NE_INVAL,	/* EXDEV (18)		*/
    832 	NE_NODEV,	/* ENODEV (19)		*/
    833 	NE_NOTDIR,	/* ENOTDIR (20)		*/
    834 	NE_ISDIR,	/* EISDIR (21)		*/
    835 	NE_INVAL,	/* EINVAL (22)		*/
    836 	NE_IO,		/* ENFILE (23)		*/
    837 	NE_IO,		/* EMFILE (24)		*/
    838 	NE_INVAL,	/* ENOTTY (25)		*/
    839 	NE_ACCES,	/* ETXTBSY (26)		*/
    840 	NE_FBIG,	/* EFBIG (27)		*/
    841 	NE_NOSPC,	/* ENOSPC (28)		*/
    842 	NE_INVAL,	/* ESPIPE (29)		*/
    843 	NE_ROFS,	/* EROFS (30)		*/
    844 	NE_INVAL,	/* EMLINK (31)		*/
    845 	NE_INVAL,	/* EPIPE (32)		*/
    846 	NE_INVAL,	/* EDOM (33)		*/
    847 	NE_INVAL,	/* ERANGE (34)		*/
    848 	NE_INVAL,	/* EDEADLK (35)		*/
    849 	NE_NAMETOOLONG,	/* ENAMETOOLONG (36)	*/
    850 	NE_INVAL,	/* ENOLCK (37)		*/
    851 	NE_INVAL,	/* ENOSYS (38)		*/
    852 	NE_NOTEMPTY,	/* ENOTEMPTY (39)	*/
    853 	NE_INVAL,	/* ELOOP (40)		*/
    854 	NE_INVAL,	/* unused (41)		*/
    855 	NE_INVAL,	/* ENOMSG (42)		*/
    856 	NE_INVAL,	/* EIDRM (43)		*/
    857 	NE_INVAL,	/* ECHRNG (44)		*/
    858 	NE_INVAL,	/* EL2NSYNC (45)	*/
    859 	NE_INVAL,	/* EL3HLT (46)		*/
    860 	NE_INVAL,	/* EL3RST (47)		*/
    861 	NE_INVAL,	/* ELNRNG (48)		*/
    862 	NE_INVAL,	/* EUNATCH (49)		*/
    863 	NE_INVAL,	/* ENOCSI (50)		*/
    864 	NE_INVAL,	/* EL2HLT (51)		*/
    865 	NE_INVAL,	/* EBADE (52)		*/
    866 	NE_INVAL,	/* EBADR (53)		*/
    867 	NE_INVAL,	/* EXFULL (54)		*/
    868 	NE_INVAL,	/* ENOANO (55)		*/
    869 	NE_INVAL,	/* EBADRQC (56)		*/
    870 	NE_INVAL,	/* EBADSLT (57)		*/
    871 	NE_INVAL,	/* unused (58)		*/
    872 	NE_INVAL,	/* EBFONT (59)		*/
    873 	NE_INVAL,	/* ENOSTR (60)		*/
    874 	NE_INVAL,	/* ENODATA (61)		*/
    875 	NE_INVAL,	/* ETIME (62)		*/
    876 	NE_INVAL,	/* ENOSR (63)		*/
    877 	NE_INVAL,	/* ENONET (64)		*/
    878 	NE_INVAL,	/* ENOPKG (65)		*/
    879 	NE_INVAL,	/* EREMOTE (66)		*/
    880 	NE_INVAL,	/* ENOLINK (67)		*/
    881 	NE_INVAL,	/* EADV (68)		*/
    882 	NE_INVAL,	/* ESRMNT (69)		*/
    883 	NE_IO,		/* ECOMM (70)		*/
    884 	NE_IO,		/* EPROTO (71)		*/
    885 	NE_IO,		/* EMULTIHOP (72)	*/
    886 	NE_IO,		/* EDOTDOT (73)		*/
    887 	NE_INVAL,	/* EBADMSG (74)		*/
    888 	NE_INVAL,	/* EOVERFLOW (75)	*/
    889 	NE_INVAL,	/* ENOTUNIQ (76)	*/
    890 	NE_INVAL,	/* EBADFD (77)		*/
    891 	NE_IO,		/* EREMCHG (78)		*/
    892 	NE_IO,		/* ELIBACC (79)		*/
    893 	NE_IO,		/* ELIBBAD (80)		*/
    894 	NE_IO,		/* ELIBSCN (81)		*/
    895 	NE_IO,		/* ELIBMAX (82)		*/
    896 	NE_IO,		/* ELIBEXEC (83)	*/
    897 	NE_INVAL,	/* EILSEQ (84)		*/
    898 	NE_INVAL,	/* ERESTART (85)	*/
    899 	NE_INVAL,	/* ESTRPIPE (86)	*/
    900 	NE_INVAL,	/* EUSERS (87)		*/
    901 	NE_INVAL,	/* ENOTSOCK (88)	*/
    902 	NE_INVAL,	/* EDESTADDRREQ (89)	*/
    903 	NE_INVAL,	/* EMSGSIZE (90)	*/
    904 	NE_INVAL,	/* EPROTOTYPE (91)	*/
    905 	NE_INVAL,	/* ENOPROTOOPT (92)	*/
    906 	NE_INVAL,	/* EPROTONOSUPPORT (93) */
    907 	NE_INVAL,	/* ESOCKTNOSUPPORT (94) */
    908 	NE_INVAL,	/* EOPNOTSUPP (95)	*/
    909 	NE_INVAL,	/* EPFNOSUPPORT (96)	*/
    910 	NE_INVAL,	/* EAFNOSUPPORT (97)	*/
    911 	NE_INVAL,	/* EADDRINUSE (98)	*/
    912 	NE_INVAL,	/* EADDRNOTAVAIL (99)	*/
    913 	NE_IO,		/* ENETDOWN (100)	*/
    914 	NE_IO,		/* ENETUNREACH (101)	*/
    915 	NE_IO,		/* ENETRESET (102)	*/
    916 	NE_IO,		/* ECONNABORTED (103)	*/
    917 	NE_IO,		/* ECONNRESET (104)	*/
    918 	NE_IO,		/* ENOBUFS (105)	*/
    919 	NE_IO,		/* EISCONN (106)	*/
    920 	NE_IO,		/* ENOTCONN (107)	*/
    921 	NE_IO,		/* ESHUTDOWN (108)	*/
    922 	NE_IO,		/* ETOOMANYREFS (109)	*/
    923 	NE_IO,		/* ETIMEDOUT (110)	*/
    924 	NE_IO,		/* ECONNREFUSED (111)	*/
    925 	NE_IO,		/* EHOSTDOWN (112)	*/
    926 	NE_IO,		/* EHOSTUNREACH (113)	*/
    927 	NE_IO,		/* EALREADY (114)	*/
    928 	NE_IO,		/* EINPROGRESS (115)	*/
    929 	NE_STALE,	/* ESTALE (116)		*/
    930 	NE_IO,		/* EUCLEAN (117)	*/
    931 	NE_INVAL,	/* ENOTNAM (118)	*/
    932 	NE_INVAL,	/* ENAVAIL (119)	*/
    933 	NE_INVAL,	/* EISNAM (120)		*/
    934 	NE_IO,		/* EREMOTEIO (121)	*/
    935 	NE_DQUOT,	/* EDQUOT (122)		*/
    936 };
    937 
    938 
    939 int
    940 linux_nfs_error(int e)
    941 {
    942   int ret = (nfsstat) NE_IO;
    943 
    944   if (e < NFS_LOMAP || e > NFS_HIMAP)
    945     ret = (nfsstat) NE_IO;
    946   else
    947     ret = nfs_errormap[e - NFS_LOMAP];
    948   dlog("linux_nfs_error: map error %d to NFS error %d", e, ret);
    949   return (nfsstat) ret;
    950 }
    951 
    952 
    953 #ifdef HAVE_LOOP_DEVICE
    954 /****************************************************************************/
    955 /*** LOOP DEVICE SUPPORT						  ***/
    956 /*** Loop Device setup code taken from mount-2.11g-5.src.rpm, which was   ***/
    957 /*** originally written bt Ted T'so and others.				  ***/
    958 /****************************************************************************/
    959 
    960 #define PROC_DEVICES	"/proc/devices"
    961 
    962 #if not_used_yet
    963 static int
    964 show_loop(char *device)
    965 {
    966   struct loop_info loopinfo;
    967   int fd;
    968 
    969   if ((fd = open(device, O_RDONLY)) < 0) {
    970     dlog("loop: can't open device %s: %m", device);
    971     return -2;
    972   }
    973   if (ioctl(fd, LOOP_GET_STATUS, &loopinfo) < 0) {
    974     dlog("loop: can't get info on device %s: %m", device);
    975     close(fd);
    976     return -1;
    977   }
    978   dlog("show_loop: %s: [%04x]:%ld (%s)",
    979        device, loopinfo.lo_device, loopinfo.lo_inode,
    980        loopinfo.lo_name);
    981 
    982   close(fd);
    983 
    984   return 0;
    985 }
    986 
    987 
    988 static int
    989 is_loop_device(const char *device)
    990 {
    991   struct stat statbuf;
    992   int loopmajor = 7;
    993 
    994   return (loopmajor && stat(device, &statbuf) == 0 &&
    995 	  S_ISBLK(statbuf.st_mode) &&
    996 	  (statbuf.st_rdev>>8) == loopmajor);
    997 }
    998 #endif /* not_used_yet */
    999 
   1000 
   1001 /*
   1002  * Just creating a device, say in /tmp, is probably a bad idea - people
   1003  * might have problems with backup or so.  So, we just try /dev/loop[0-7].
   1004  */
   1005 static char *
   1006 find_unused_loop_device(void)
   1007 {
   1008   char dev[20];
   1009   char *loop_formats[] = { "/dev/loop%d", "/dev/loop/%d" };
   1010   int i, j, fd, somedev = 0, someloop = 0, loop_known = 0;
   1011   struct stat statbuf;
   1012   struct loop_info loopinfo;
   1013   FILE *procdev;
   1014 
   1015 #define LOOP_FMT_SIZE(a) (sizeof(a)/sizeof(a[0]))
   1016   for (j = 0; j < (int) LOOP_FMT_SIZE(loop_formats); j++) {
   1017     for (i = 0; i < 256; i++) {
   1018       xsnprintf(dev, sizeof(dev), loop_formats[j], i);
   1019       if (stat(dev, &statbuf) == 0 && S_ISBLK(statbuf.st_mode)) {
   1020 	somedev++;
   1021 	fd = open(dev, O_RDONLY);
   1022 	if (fd >= 0) {
   1023 	  if (ioctl(fd, LOOP_GET_STATUS, &loopinfo) == 0)
   1024 	    someloop++;		/* in use */
   1025 	  else if (errno == ENXIO) {
   1026 	    close(fd);
   1027 	    return xstrdup(dev); /* probably free */
   1028 	  }
   1029 	  close(fd);
   1030 	}
   1031 	continue;		/* continue trying as long as devices exist */
   1032       }
   1033       break;
   1034     }
   1035   }
   1036 
   1037   /* Nothing found. Why not? */
   1038   if ((procdev = fopen(PROC_DEVICES, "r")) != NULL) {
   1039     char line[100];
   1040     while (fgets(line, sizeof(line), procdev))
   1041       if (strstr(line, " loop\n")) {
   1042 	loop_known = 1;
   1043 	break;
   1044       }
   1045     fclose(procdev);
   1046     if (!loop_known)
   1047       loop_known = -1;
   1048   }
   1049 
   1050   if (!somedev) {
   1051     dlog("Could not find any device /dev/loop#");
   1052   } else if (!someloop) {
   1053     if (loop_known == 1) {
   1054       dlog("Could not find any loop device.");
   1055       dlog("...Maybe /dev/loop# has a wrong major number?");
   1056     }
   1057     else if (loop_known == -1) {
   1058       dlog("Could not find any loop device, and, according to %s,", PROC_DEVICES);
   1059       dlog("...this kernel does not know about the loop device.");
   1060       dlog("... (If so, then recompile or `insmod loop.o'.)");
   1061     } else {
   1062       dlog("Could not find any loop device. Maybe this kernel does not know,");
   1063       dlog("...about the loop device (then recompile or `insmod loop.o'), or");
   1064       dlog("...maybe /dev/loop# has the wrong major number?");
   1065     }
   1066   } else {
   1067     dlog("Could not find any free loop device!");
   1068   }
   1069   return NULL;
   1070 }
   1071 
   1072 
   1073 /* returns 0 if OK, -1 otherwise */
   1074 char *
   1075 setup_loop_device(const char *file)
   1076 {
   1077   struct loop_info loopinfo;
   1078   int fd, ffd, mode, err = -1;
   1079   char *device = find_unused_loop_device();
   1080 
   1081   if (!device) {
   1082     dlog("no unused loop device");
   1083     goto out;
   1084   }
   1085 
   1086   mode = O_RDWR | O_LARGEFILE;
   1087   if ((ffd = open(file, mode)) < 0) {
   1088     if (errno == EROFS) {
   1089       mode = O_RDONLY | O_LARGEFILE;
   1090       ffd = open(file, mode);
   1091     }
   1092     if (ffd < 0) {
   1093       dlog("%s: %m", file);
   1094       goto out;
   1095     }
   1096   }
   1097   if ((fd = open(device, mode)) < 0) {
   1098     dlog("%s: %m", device);
   1099     goto out_close;
   1100   }
   1101 
   1102   memset(&loopinfo, 0, sizeof(loopinfo));
   1103   xstrlcpy(loopinfo.lo_name, file, LO_NAME_SIZE);
   1104   loopinfo.lo_offset = 0;
   1105 
   1106   if (ioctl(fd, LOOP_SET_FD, ffd) < 0) {
   1107     dlog("ioctl: LOOP_SET_FD: %m");
   1108     goto out_close_all;
   1109   }
   1110   if (ioctl(fd, LOOP_SET_STATUS, &loopinfo) < 0) {
   1111     (void) ioctl(fd, LOOP_CLR_FD, 0);
   1112     dlog("ioctl: LOOP_SET_STATUS: %m");
   1113     goto out_close_all;
   1114   }
   1115 
   1116   /* if gets here, all is OK */
   1117   err = 0;
   1118 
   1119 out_close_all:
   1120   close(fd);
   1121 out_close:
   1122   close(ffd);
   1123 out:
   1124 
   1125   if (err) {
   1126     XFREE(device);
   1127     return NULL;
   1128   } else {
   1129     dlog("setup_loop_device(%s,%s): success", device, file);
   1130     return device;
   1131   }
   1132 }
   1133 
   1134 
   1135 int
   1136 delete_loop_device(const char *device)
   1137 {
   1138   int fd;
   1139 
   1140   if ((fd = open(device, O_RDONLY)) < 0) {
   1141     dlog("delete_loop_device: can't delete device %s: %m", device);
   1142     return -1;
   1143   }
   1144   if (ioctl(fd, LOOP_CLR_FD, 0) < 0) {
   1145     dlog("ioctl: LOOP_CLR_FD: %m");
   1146     return -1;
   1147   }
   1148   close(fd);
   1149   dlog("delete_loop_device(%s): success", device);
   1150   return 0;
   1151 }
   1152 #endif /* HAVE_LOOP_DEVICE */
   1153 
   1154 
   1155 /****************************************************************************/
   1156