Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 | /* * linux/fs/umsdos/ioctl.c * * Written 1993 by Jacques Gelinas * * Extended MS-DOS ioctl directory handling functions */ #include <asm/uaccess.h> #include <linux/errno.h> #include <linux/mm.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/fs.h> #include <linux/msdos_fs.h> #include <linux/umsdos_fs.h> #define PRINTK(x) #define Printk(x) printk x struct UMSDOS_DIR_ONCE { struct dirent *ent; int count; }; /* Record a single entry the first call. Return -EINVAL the next one. */ static int umsdos_ioctl_fill( void * buf, const char * name, int name_len, off_t offset, ino_t ino) { int ret = -EINVAL; struct UMSDOS_DIR_ONCE *d = (struct UMSDOS_DIR_ONCE *)buf; if (d->count == 0){ copy_to_user (d->ent->d_name,name,name_len); put_user ('\0',d->ent->d_name+name_len); put_user (name_len,&d->ent->d_reclen); put_user (ino,&d->ent->d_ino); put_user (offset,&d->ent->d_off); d->count = 1; ret = 0; } return ret; } /* Perform special function on a directory */ int UMSDOS_ioctl_dir ( struct inode *dir, struct file *filp, unsigned int cmd, unsigned long data) { int ret = -EPERM; int err; /* #Specification: ioctl / acces Only root (effective id) is allowed to do IOCTL on directory in UMSDOS. EPERM is returned for other user. */ /* Well, not all cases require write access, but it simplifies the code, and let's face it, there is only one client (umssync) for all this. */ if ((err = verify_area(VERIFY_WRITE,(void*)data,sizeof(struct umsdos_ioctl))) < 0) { ret = err; }else if (current->euid == 0 || cmd == UMSDOS_GETVERSION){ struct umsdos_ioctl *idata = (struct umsdos_ioctl *)data; ret = -EINVAL; /* #Specification: ioctl / prototypes The official prototype for the umsdos ioctl on directory is: int ioctl ( int fd, // File handle of the directory int cmd, // command struct umsdos_ioctl *data) The struct and the commands are defined in linux/umsdos_fs.h. umsdos_progs/umsdosio.c provide an interface in C++ to all these ioctl. umsdos_progs/udosctl is a small utility showing all this. These ioctl generally allow one to work on the EMD or the DOS directory independently. These are essential to implement the synchronise. */ PRINTK (("ioctl %d ",cmd)); if (cmd == UMSDOS_GETVERSION){ /* #Specification: ioctl / UMSDOS_GETVERSION The field version and release of the structure umsdos_ioctl are filled with the version and release number of the fs code in the kernel. This will allow some form of checking. Users won't be able to run incompatible utility such as the synchroniser (umssync). umsdos_progs/umsdosio.c enforce this checking. Return always 0. */ put_user(UMSDOS_VERSION,&idata->version); put_user(UMSDOS_RELEASE,&idata->release); ret = 0; }else if (cmd == UMSDOS_READDIR_DOS){ /* #Specification: ioctl / UMSDOS_READDIR_DOS One entry is read from the DOS directory at the current file position. The entry is put as is in the dos_dirent field of struct umsdos_ioctl. Return > 0 if success. */ struct UMSDOS_DIR_ONCE bufk; bufk.count = 0; bufk.ent = &idata->dos_dirent; fat_readdir(dir,filp,&bufk,umsdos_ioctl_fill); ret = bufk.count == 1 ? 1 : 0; }else if (cmd == UMSDOS_READDIR_EMD){ /* #Specification: ioctl / UMSDOS_READDIR_EMD One entry is read from the EMD at the current file position. The entry is put as is in the umsdos_dirent field of struct umsdos_ioctl. The corresponding mangled DOS entry name is put in the dos_dirent field. All entries are read including hidden links. Blank entries are skipped. Return > 0 if success. */ struct inode *emd_dir = umsdos_emd_dir_lookup (dir,0); if (emd_dir != NULL){ while (1){ if (filp->f_pos >= emd_dir->i_size){ ret = 0; break; }else{ struct umsdos_dirent entry; off_t f_pos = filp->f_pos; ret = umsdos_emd_dir_readentry (emd_dir,filp,&entry); if (ret < 0){ break; }else if (entry.name_len > 0){ struct umsdos_info info; ret = entry.name_len; umsdos_parse (entry.name,entry.name_len,&info); info.f_pos = f_pos; umsdos_manglename(&info); copy_to_user(&idata->umsdos_dirent,&entry ,sizeof(entry)); copy_to_user(&idata->dos_dirent.d_name ,info.fake.fname,info.fake.len+1); break; } } } iput (emd_dir); }else{ /* The absence of the EMD is simply seen as an EOF */ ret = 0; } }else if (cmd == UMSDOS_INIT_EMD){ /* #Specification: ioctl / UMSDOS_INIT_EMD The UMSDOS_INIT_EMD command make sure the EMD exist for a directory. If it does not, it is created. Also, it makes sure the directory functions table (struct inode_operations) is set to the UMSDOS semantic. This mean that umssync may be applied to an "opened" msdos directory, and it will change behavior on the fly. Return 0 if success. */ extern struct inode_operations umsdos_rdir_inode_operations; struct inode *emd_dir = umsdos_emd_dir_lookup (dir,1); ret = emd_dir != NULL; iput (emd_dir); dir->i_op = ret ? &umsdos_dir_inode_operations : &umsdos_rdir_inode_operations; }else{ struct umsdos_ioctl data; copy_from_user (&data,idata,sizeof(data)); if (cmd == UMSDOS_CREAT_EMD){ /* #Specification: ioctl / UMSDOS_CREAT_EMD The umsdos_dirent field of the struct umsdos_ioctl is used as is to create a new entry in the EMD of the directory. The DOS directory is not modified. No validation is done (yet). Return 0 if success. */ struct umsdos_info info; /* This makes sure info.entry and info in general is correctly */ /* initialised */ memcpy (&info.entry,&data.umsdos_dirent ,sizeof(data.umsdos_dirent)); umsdos_parse (data.umsdos_dirent.name ,data.umsdos_dirent.name_len,&info); ret = umsdos_newentry (dir,&info); }else if (cmd == UMSDOS_RENAME_DOS){ /* #Specification: ioctl / UMSDOS_RENAME_DOS A file or directory is rename in a DOS directory (not moved across directory). The source name is in the dos_dirent.name field and the destination is in umsdos_dirent.name field. This ioctl allows umssync to rename a mangle file name before syncing it back in the EMD. */ dir->i_count += 2; ret = msdos_rename (dir ,data.dos_dirent.d_name,data.dos_dirent.d_reclen ,dir ,data.umsdos_dirent.name,data.umsdos_dirent.name_len,0); }else if (cmd == UMSDOS_UNLINK_EMD){ /* #Specification: ioctl / UMSDOS_UNLINK_EMD The umsdos_dirent field of the struct umsdos_ioctl is used as is to remove an entry from the EMD of the directory. No validation is done (yet). The mode field is used to validate S_ISDIR or S_ISREG. Return 0 if success. */ struct umsdos_info info; /* This makes sure info.entry and info in general is correctly */ /* initialised */ memcpy (&info.entry,&data.umsdos_dirent ,sizeof(data.umsdos_dirent)); umsdos_parse (data.umsdos_dirent.name ,data.umsdos_dirent.name_len,&info); ret = umsdos_delentry (dir,&info ,S_ISDIR(data.umsdos_dirent.mode)); }else if (cmd == UMSDOS_UNLINK_DOS){ /* #Specification: ioctl / UMSDOS_UNLINK_DOS The dos_dirent field of the struct umsdos_ioctl is used to execute a msdos_unlink operation. The d_name and d_reclen fields are used. Return 0 if success. */ dir->i_count++; ret = msdos_unlink (dir,data.dos_dirent.d_name ,data.dos_dirent.d_reclen); }else if (cmd == UMSDOS_RMDIR_DOS){ /* #Specification: ioctl / UMSDOS_RMDIR_DOS The dos_dirent field of the struct umsdos_ioctl is used to execute a msdos_unlink operation. The d_name and d_reclen fields are used. Return 0 if success. */ dir->i_count++; ret = msdos_rmdir (dir,data.dos_dirent.d_name ,data.dos_dirent.d_reclen); }else if (cmd == UMSDOS_STAT_DOS){ /* #Specification: ioctl / UMSDOS_STAT_DOS The dos_dirent field of the struct umsdos_ioctl is used to execute a stat operation in the DOS directory. The d_name and d_reclen fields are used. The following field of umsdos_ioctl.stat are filled. st_ino,st_mode,st_size,st_atime,st_mtime,st_ctime, Return 0 if success. */ struct inode *inode; ret = umsdos_real_lookup (dir,data.dos_dirent.d_name ,data.dos_dirent.d_reclen,&inode); if (ret == 0){ data.stat.st_ino = inode->i_ino; data.stat.st_mode = inode->i_mode; data.stat.st_size = inode->i_size; data.stat.st_atime = inode->i_atime; data.stat.st_ctime = inode->i_ctime; data.stat.st_mtime = inode->i_mtime; copy_to_user (&idata->stat,&data.stat,sizeof(data.stat)); iput (inode); } }else if (cmd == UMSDOS_DOS_SETUP){ /* #Specification: ioctl / UMSDOS_DOS_SETUP The UMSDOS_DOS_SETUP ioctl allow changing the default permission of the MsDOS file system driver on the fly. The MsDOS driver apply global permission to every file and directory. Normally these permissions are controlled by a mount option. This is not available for root partition, so a special utility (umssetup) is provided to do this, normally in /etc/rc.local. Be aware that this apply ONLY to MsDOS directory (those without EMD --linux-.---). Umsdos directory have independent (standard) permission for each and every file. The field umsdos_dirent provide the information needed. umsdos_dirent.uid and gid sets the owner and group. umsdos_dirent.mode set the permissions flags. */ dir->i_sb->u.msdos_sb.options.fs_uid = data.umsdos_dirent.uid; dir->i_sb->u.msdos_sb.options.fs_gid = data.umsdos_dirent.gid; dir->i_sb->u.msdos_sb.options.fs_umask = data.umsdos_dirent.mode; ret = 0; } } } PRINTK (("ioctl return %d\n",ret)); return ret; } |