The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/fs/umsdos/ioctl.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*
    2  *  linux/fs/umsdos/ioctl.c
    3  *
    4  *  Written 1993 by Jacques Gelinas
    5  *
    6  *  Extended MS-DOS ioctl directory handling functions
    7  */
    8 
    9 #include <asm/uaccess.h>
   10 #include <linux/errno.h>
   11 #include <linux/mm.h>
   12 #include <linux/kernel.h>
   13 #include <linux/sched.h>
   14 #include <linux/fs.h>
   15 #include <linux/msdos_fs.h>
   16 #include <linux/umsdos_fs.h>
   17 
   18 struct UMSDOS_DIR_ONCE {
   19         struct dirent *ent;
   20         int count;
   21 };
   22 
   23 /*
   24  * Record a single entry the first call.
   25  * Return -EINVAL the next one.
   26  */
   27 static int umsdos_ioctl_fill (
   28                                      void *buf,
   29                                      const char *name,
   30                                      int name_len,
   31                                      loff_t offset,
   32                                      ino_t ino,
   33                                      unsigned type)
   34 {
   35         int ret = -EINVAL;
   36         struct UMSDOS_DIR_ONCE *d = (struct UMSDOS_DIR_ONCE *) buf;
   37 
   38         if (d->count == 0) {
   39                 copy_to_user (d->ent->d_name, name, name_len);
   40                 put_user ('\0', d->ent->d_name + name_len);
   41                 put_user (name_len, &d->ent->d_reclen);
   42                 put_user (ino, &d->ent->d_ino);
   43                 put_user (offset, &d->ent->d_off);
   44                 d->count = 1;
   45                 ret = 0;
   46         }
   47         return ret;
   48 }
   49 
   50 
   51 /*
   52  * Perform special function on a directory
   53  */
   54 /* #Specification: ioctl / prototypes
   55  * The official prototype for the umsdos ioctl on directory
   56  * is:
   57  * 
   58  * int ioctl (
   59  * int fd,          // File handle of the directory
   60  * int cmd, // command
   61  * struct umsdos_ioctl *data)
   62  * 
   63  * The struct and the commands are defined in linux/umsdos_fs.h.
   64  * 
   65  * umsdos_progs/umsdosio.c provide an interface in C++ to all
   66  * these ioctl. umsdos_progs/udosctl is a small utility showing
   67  * all this.
   68  * 
   69  * These ioctl generally allow one to work on the EMD or the
   70  * DOS directory independently. These are essential to implement
   71  * the synchronise.
   72  */
   73 int UMSDOS_ioctl_dir(struct inode *dir, struct file *filp, unsigned int cmd,
   74                         unsigned long data_ptr)
   75 {
   76         struct dentry *dentry = filp->f_dentry;
   77         struct umsdos_ioctl *idata = (struct umsdos_ioctl *) data_ptr;
   78         int ret;
   79         struct umsdos_ioctl data;
   80 
   81 Printk(("UMSDOS_ioctl_dir: %s/%s, cmd=%d, data=%08lx\n",
   82 dentry->d_parent->d_name.name, dentry->d_name.name, cmd, data_ptr));
   83 
   84         /* forward non-umsdos ioctls - this hopefully doesn't cause conflicts */
   85         if (cmd != UMSDOS_GETVERSION
   86             && cmd != UMSDOS_READDIR_DOS
   87             && cmd != UMSDOS_READDIR_EMD
   88             && cmd != UMSDOS_INIT_EMD
   89             && cmd != UMSDOS_CREAT_EMD
   90             && cmd != UMSDOS_RENAME_DOS
   91             && cmd != UMSDOS_UNLINK_EMD
   92             && cmd != UMSDOS_UNLINK_DOS
   93             && cmd != UMSDOS_RMDIR_DOS
   94             && cmd != UMSDOS_STAT_DOS
   95             && cmd != UMSDOS_DOS_SETUP)
   96                 return fat_dir_ioctl (dir, filp, cmd, data_ptr);
   97 
   98         /* #Specification: ioctl / access
   99          * Only root (effective id) is allowed to do IOCTL on directory
  100          * in UMSDOS. EPERM is returned for other user.
  101          */
  102         /*
  103          * Well, not all cases require write access, but it simplifies
  104          * the code, and let's face it, there is only one client (umssync)
  105          * for all this.
  106          */
  107         ret = verify_area (VERIFY_WRITE, (void *) data_ptr, 
  108                                 sizeof (struct umsdos_ioctl));
  109         if (ret < 0)
  110                 goto out;
  111 
  112         ret = -EPERM;
  113         if (current->euid != 0 && cmd != UMSDOS_GETVERSION)
  114                 goto out;
  115 
  116         ret = -EINVAL;
  117         if (cmd == UMSDOS_GETVERSION) {
  118                 /* #Specification: ioctl / UMSDOS_GETVERSION
  119                  * The field version and release of the structure
  120                  * umsdos_ioctl are filled with the version and release
  121                  * number of the fs code in the kernel. This will allow
  122                  * some form of checking. Users won't be able to run
  123                  * incompatible utility such as the synchroniser (umssync).
  124                  * umsdos_progs/umsdosio.c enforce this checking.
  125                  * 
  126                  * Return always 0.
  127                  */
  128                 put_user (UMSDOS_VERSION, &idata->version);
  129                 put_user (UMSDOS_RELEASE, &idata->release);
  130                 ret = 0;
  131                 goto out;
  132         }
  133         if (cmd == UMSDOS_READDIR_DOS) {
  134                 /* #Specification: ioctl / UMSDOS_READDIR_DOS
  135                  * One entry is read from the DOS directory at the current
  136                  * file position. The entry is put as is in the dos_dirent
  137                  * field of struct umsdos_ioctl.
  138                  * 
  139                  * Return > 0 if success.
  140                  */
  141                 struct UMSDOS_DIR_ONCE bufk;
  142 
  143                 bufk.count = 0;
  144                 bufk.ent = &idata->dos_dirent;
  145 
  146                 fat_readdir (filp, &bufk, umsdos_ioctl_fill);
  147 
  148                 ret = bufk.count == 1 ? 1 : 0;
  149                 goto out;
  150         }
  151         if (cmd == UMSDOS_READDIR_EMD) {
  152                 /* #Specification: ioctl / UMSDOS_READDIR_EMD
  153                  * One entry is read from the EMD at the current
  154                  * file position. The entry is put as is in the umsdos_dirent
  155                  * field of struct umsdos_ioctl. The corresponding mangled
  156                  * DOS entry name is put in the dos_dirent field.
  157                  * 
  158                  * All entries are read including hidden links. Blank
  159                  * entries are skipped.
  160                  * 
  161                  * Return > 0 if success.
  162                  */
  163                 struct dentry *demd;
  164                 loff_t pos = filp->f_pos;
  165 
  166                 /* The absence of the EMD is simply seen as an EOF */
  167                 demd = umsdos_get_emd_dentry(dentry);
  168                 ret = PTR_ERR(demd);
  169                 if (IS_ERR(demd))
  170                         goto out;
  171                 ret = 0;
  172                 if (!demd->d_inode)
  173                         goto read_dput;
  174 
  175                 while (pos < demd->d_inode->i_size) {
  176                         off_t f_pos = pos;
  177                         struct umsdos_dirent entry;
  178                         struct umsdos_info info;
  179 
  180                         ret = umsdos_emd_dir_readentry (demd, &pos, &entry);
  181 
  182                         if (ret == -ENAMETOOLONG) {
  183                                 printk (KERN_INFO "Fixing EMD entry with invalid size -- zeroing out\n");
  184                                 memset (&info, 0, sizeof (info));
  185                                 info.f_pos = f_pos;
  186                                 info.recsize = UMSDOS_REC_SIZE;
  187                                 ret = umsdos_writeentry (dentry, &info, 1);
  188                                 continue;
  189                         }
  190 
  191                         if (ret)
  192                                 break;
  193                         if (entry.name_len <= 0)
  194                                 continue;
  195 
  196                         umsdos_parse (entry.name, entry.name_len, &info);
  197                         info.f_pos = f_pos;
  198                         umsdos_manglename (&info);
  199                         ret = -EFAULT;
  200                         if (copy_to_user (&idata->umsdos_dirent, &entry,
  201                                                         sizeof (entry)))
  202                                 break;
  203                         if (copy_to_user (&idata->dos_dirent.d_name,
  204                                                         info.fake.fname,
  205                                                         info.fake.len + 1))
  206                                 break;
  207                         ret = entry.name_len;
  208                         break;
  209                 }
  210                 /* update the original f_pos */
  211                 filp->f_pos = pos;
  212         read_dput:
  213                 d_drop(demd);
  214                 dput(demd);
  215                 goto out;
  216         }
  217         if (cmd == UMSDOS_INIT_EMD) {
  218                 /* #Specification: ioctl / UMSDOS_INIT_EMD
  219                  * The UMSDOS_INIT_EMD command makes sure the EMD
  220                  * exists for a directory. If it does not, it is
  221                  * created. Also, it makes sure the directory function
  222                  * table (struct inode_operations) is set to the UMSDOS
  223                  * semantic. This mean that umssync may be applied to
  224                  * an "opened" msdos directory, and it will change behavior
  225                  * on the fly.
  226                  * 
  227                  * Return 0 if success.
  228                  */
  229 
  230                 ret = umsdos_make_emd(dentry);
  231 Printk(("UMSDOS_ioctl_dir: INIT_EMD %s/%s, ret=%d\n",
  232 dentry->d_parent->d_name.name, dentry->d_name.name, ret));
  233                 umsdos_setup_dir (dentry);
  234                 goto out;
  235         }
  236 
  237         ret = -EFAULT;
  238         if (copy_from_user (&data, idata, sizeof (data)))
  239                 goto out;
  240 
  241         if (cmd == UMSDOS_CREAT_EMD) {
  242                 /* #Specification: ioctl / UMSDOS_CREAT_EMD
  243                  * The umsdos_dirent field of the struct umsdos_ioctl is used
  244                  * as is to create a new entry in the EMD of the directory.
  245                  * The DOS directory is not modified.
  246                  * No validation is done (yet).
  247                  * 
  248                  * Return 0 if success.
  249                  */
  250                 struct umsdos_info info;
  251 
  252                 /* This makes sure info.entry and info in general
  253                  * is correctly initialised
  254                  */
  255                 memcpy (&info.entry, &data.umsdos_dirent,
  256                         sizeof (data.umsdos_dirent));
  257                 umsdos_parse (data.umsdos_dirent.name
  258                     ,data.umsdos_dirent.name_len, &info);
  259                 ret = umsdos_newentry (dentry, &info);
  260                 goto out;
  261         }
  262         else if (cmd == UMSDOS_RENAME_DOS) {
  263                 struct dentry *old_dentry, *new_dentry;         /* FIXME */
  264 
  265                 /* #Specification: ioctl / UMSDOS_RENAME_DOS
  266                  * A file or directory is renamed in a DOS directory
  267                  * (not moved across directory). The source name
  268                  * is in the dos_dirent.name field and the destination
  269                  * is in umsdos_dirent.name field.
  270                  * 
  271                  * This ioctl allows umssync to rename a mangled file
  272                  * name before syncing it back in the EMD.
  273                  */
  274                 old_dentry = umsdos_lookup_dentry (dentry, 
  275                                                 data.dos_dirent.d_name,
  276                                                 data.dos_dirent.d_reclen ,1);
  277                 ret = PTR_ERR(old_dentry);
  278                 if (IS_ERR(old_dentry))
  279                         goto out;
  280                 new_dentry = umsdos_lookup_dentry (dentry,
  281                                                 data.umsdos_dirent.name,
  282                                                 data.umsdos_dirent.name_len, 1);
  283                 ret = PTR_ERR(new_dentry);
  284                 if (!IS_ERR(new_dentry)) {
  285 printk("umsdos_ioctl: renaming %s/%s to %s/%s\n",
  286 old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
  287 new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
  288                         ret = msdos_rename (dir, old_dentry, dir, new_dentry);
  289                         d_drop(new_dentry);
  290                         d_drop(old_dentry);
  291                         dput(new_dentry);
  292                 }
  293                 dput(old_dentry);
  294                 goto out;
  295         }
  296         else if (cmd == UMSDOS_UNLINK_EMD) {
  297                 /* #Specification: ioctl / UMSDOS_UNLINK_EMD
  298                  * The umsdos_dirent field of the struct umsdos_ioctl is used
  299                  * as is to remove an entry from the EMD of the directory.
  300                  * No validation is done (yet). The mode field is used
  301                  * to validate S_ISDIR or S_ISREG.
  302                  * 
  303                  * Return 0 if success.
  304                  */
  305                 struct umsdos_info info;
  306 
  307                 /* This makes sure info.entry and info in general
  308                  * is correctly initialised
  309                  */
  310                 memcpy (&info.entry, &data.umsdos_dirent,
  311                         sizeof (data.umsdos_dirent));
  312                 umsdos_parse (data.umsdos_dirent.name,
  313                                 data.umsdos_dirent.name_len, &info);
  314                 ret = umsdos_delentry (dentry, &info,
  315                                 S_ISDIR (data.umsdos_dirent.mode));
  316                 if (ret) {
  317                         printk(KERN_WARNING
  318                                 "umsdos_ioctl: delentry %s/%s failed, ret=%d\n",
  319                                 dentry->d_name.name, info.entry.name, ret);
  320                 }
  321                 goto out;
  322         }
  323         else if (cmd == UMSDOS_UNLINK_DOS) {
  324                 struct dentry *temp;
  325 
  326                 /* #Specification: ioctl / UMSDOS_UNLINK_DOS
  327                  * The dos_dirent field of the struct umsdos_ioctl is used to
  328                  * execute a msdos_unlink operation. The d_name and d_reclen
  329                  * fields are used.
  330                  * 
  331                  * Return 0 if success.
  332                  */
  333                 temp = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name,
  334                                                 data.dos_dirent.d_reclen, 1);
  335                 ret = PTR_ERR(temp);
  336                 if (IS_ERR(temp))
  337                         goto out;
  338                 ret = -ENOENT;
  339                 if (temp->d_inode) {
  340                         ret = -EISDIR;
  341                         if (!S_ISDIR(temp->d_inode->i_mode))
  342                                 ret = msdos_unlink (dir, temp);
  343                         if (!ret)
  344                                 d_delete(temp);
  345                 }
  346                 dput (temp);
  347                 goto out;
  348         }
  349         else if (cmd == UMSDOS_RMDIR_DOS) {
  350                 struct dentry *temp;
  351 
  352                 /* #Specification: ioctl / UMSDOS_RMDIR_DOS
  353                  * The dos_dirent field of the struct umsdos_ioctl is used to
  354                  * execute a msdos_rmdir operation. The d_name and d_reclen
  355                  * fields are used.
  356                  * 
  357                  * Return 0 if success.
  358                  */
  359                 temp = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name,
  360                                             data.dos_dirent.d_reclen, 1);
  361                 ret = PTR_ERR(temp);
  362                 if (IS_ERR(temp))
  363                         goto out;
  364                 ret = -ENOENT;
  365                 if (temp->d_inode) {
  366                         ret = -ENOTDIR;
  367                         if (S_ISDIR(temp->d_inode->i_mode))
  368                                 ret = msdos_rmdir (dir, temp);
  369                         if (!ret)
  370                                 d_delete(temp);
  371                 }
  372                 dput (temp);
  373                 goto out;
  374 
  375         } else if (cmd == UMSDOS_STAT_DOS) {
  376                 /* #Specification: ioctl / UMSDOS_STAT_DOS
  377                  * The dos_dirent field of the struct umsdos_ioctl is
  378                  * used to execute a stat operation in the DOS directory.
  379                  * The d_name and d_reclen fields are used.
  380                  * 
  381                  * The following field of umsdos_ioctl.stat are filled.
  382                  * 
  383                  * st_ino,st_mode,st_size,st_atime,st_mtime,st_ctime,
  384                  * Return 0 if success.
  385                  */
  386                 struct dentry *dret;
  387                 struct inode *inode;
  388 
  389                 dret = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name,
  390                                             data.dos_dirent.d_reclen, 1);
  391                 ret = PTR_ERR(dret);
  392                 if (IS_ERR(dret))
  393                         goto out;
  394                 ret = -ENOENT;
  395                 inode = dret->d_inode;
  396                 if (inode) {
  397                         data.stat.st_ino = inode->i_ino;
  398                         data.stat.st_mode = inode->i_mode;
  399                         data.stat.st_size = inode->i_size;
  400                         data.stat.st_atime = inode->i_atime;
  401                         data.stat.st_ctime = inode->i_ctime;
  402                         data.stat.st_mtime = inode->i_mtime;
  403                         ret = -EFAULT;
  404                         if (!copy_to_user (&idata->stat, &data.stat, 
  405                                                 sizeof (data.stat)))
  406                                 ret = 0;
  407                 }
  408                 dput(dret);
  409                 goto out;
  410         }
  411         else if (cmd == UMSDOS_DOS_SETUP) {
  412                 /* #Specification: ioctl / UMSDOS_DOS_SETUP
  413                  * The UMSDOS_DOS_SETUP ioctl allow changing the
  414                  * default permission of the MS-DOS filesystem driver
  415                  * on the fly.  The MS-DOS driver applies global permissions
  416                  * to every file and directory. Normally these permissions
  417                  * are controlled by a mount option. This is not
  418                  * available for root partition, so a special utility
  419                  * (umssetup) is provided to do this, normally in
  420                  * /etc/rc.local.
  421                  * 
  422                  * Be aware that this applies ONLY to MS-DOS directories
  423                  * (those without EMD --linux-.---). Umsdos directory
  424                  * have independent (standard) permission for each
  425                  * and every file.
  426                  * 
  427                  * The field umsdos_dirent provide the information needed.
  428                  * umsdos_dirent.uid and gid sets the owner and group.
  429                  * umsdos_dirent.mode set the permissions flags.
  430                  */
  431                 dir->i_sb->u.msdos_sb.options.fs_uid = data.umsdos_dirent.uid;
  432                 dir->i_sb->u.msdos_sb.options.fs_gid = data.umsdos_dirent.gid;
  433                 dir->i_sb->u.msdos_sb.options.fs_umask = data.umsdos_dirent.mode;
  434                 ret = 0;
  435         }
  436 out:
  437         Printk (("ioctl %d, returning %d\n", cmd, ret));
  438         return ret;
  439 }

Cache object: ca47f00302609ac6ba1029d82f288e87


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.