root/fs/umsdos/emd.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. umsdos_file_read_kmem
  2. umsdos_file_write_kmem
  3. umsdos_emd_dir_write
  4. umsdos_emd_dir_read
  5. umsdos_emd_dir_lookup
  6. umsdos_emd_dir_readentry
  7. umsdos_writeentry
  8. umsdos_fillbuf
  9. umsdos_find
  10. umsdos_newentry
  11. umsdos_newhidden
  12. umsdos_delentry
  13. umsdos_isempty
  14. umsdos_findentry

   1 /*
   2  *  linux/fs/umsdos/emd.c
   3  *
   4  *  Written 1993 by Jacques Gelinas
   5  *
   6  *  Extended MS-DOS directory handling functions
   7  */
   8 #ifdef MODULE
   9 #include <linux/module.h>
  10 #endif
  11 
  12 #include <linux/types.h>
  13 #include <linux/fcntl.h>
  14 #include <linux/kernel.h>
  15 #include <asm/segment.h>
  16 #include <linux/sched.h>
  17 #include <linux/errno.h>
  18 #include <linux/string.h>
  19 #include <linux/msdos_fs.h>
  20 #include <linux/umsdos_fs.h>
  21 
  22 #define PRINTK(x)
  23 #define Printk(x) printk x
  24 
  25 /*
  26         Read a file into kernel space memory
  27 */
  28 int umsdos_file_read_kmem(
     /* [previous][next][first][last][top][bottom][index][help] */
  29         struct inode *inode,
  30         struct file *filp,
  31         char *buf,
  32         int count)
  33 {
  34         int ret;
  35         int old_fs = get_fs();  
  36         set_fs (KERNEL_DS);
  37         ret = msdos_file_read(inode,filp,buf,count);
  38         set_fs (old_fs);
  39         return ret;
  40 }
  41 /*
  42         Write to a file from kernel space
  43 */
  44 int umsdos_file_write_kmem(
     /* [previous][next][first][last][top][bottom][index][help] */
  45         struct inode *inode,
  46         struct file *filp,
  47         const char *buf,
  48         int count)
  49 {
  50         int ret;
  51         int old_fs = get_fs();
  52         set_fs (KERNEL_DS);
  53         ret = msdos_file_write(inode,filp,buf,count);
  54         set_fs (old_fs);
  55         return ret;
  56 }
  57 
  58 
  59 /*
  60         Write a block of bytes into one EMD file.
  61         The block of data is NOT in user space.
  62 
  63         Return 0 if ok, a negative error code if not.
  64 */
  65 int umsdos_emd_dir_write (
     /* [previous][next][first][last][top][bottom][index][help] */
  66         struct inode *emd_dir,
  67         struct file *filp,
  68         char *buf,      /* buffer in kernel memory, not in user space */
  69         int count)
  70 {
  71         int written;
  72         filp->f_flags = 0;
  73         written = umsdos_file_write_kmem (emd_dir,filp,buf,count);
  74         return written != count ? -EIO : 0;
  75 }
  76 /*
  77         Read a block of bytes from one EMD file.
  78         The block of data is NOT in user space.
  79         Return 0 if ok, -EIO if any error.
  80 */
  81 int umsdos_emd_dir_read (
     /* [previous][next][first][last][top][bottom][index][help] */
  82         struct inode *emd_dir,
  83         struct file *filp,
  84         char *buf,      /* buffer in kernel memory, not in user space */
  85         int count)
  86 {
  87         int ret = 0;
  88         int sizeread;
  89         filp->f_flags = 0;
  90         sizeread = umsdos_file_read_kmem (emd_dir,filp,buf,count);
  91         if (sizeread != count){
  92                 printk ("UMSDOS: problem with EMD file. Can't read pos = %Ld (%d != %d)\n"
  93                         ,filp->f_pos,sizeread,count);
  94                 ret = -EIO;
  95         }
  96         return ret;
  97 
  98 }
  99 /*
 100         Locate the EMD file in a directory and optionally, creates it.
 101 
 102         Return NULL if error. If ok, dir->u.umsdos_i.emd_inode 
 103 */
 104 struct inode *umsdos_emd_dir_lookup(struct inode *dir, int creat)
     /* [previous][next][first][last][top][bottom][index][help] */
 105 {
 106         struct inode *ret = NULL;
 107         if (dir->u.umsdos_i.i_emd_dir != 0){
 108                 ret = iget (dir->i_sb,dir->u.umsdos_i.i_emd_dir);
 109                 PRINTK (("deja trouve %d %x [%d] "
 110                         ,dir->u.umsdos_i.i_emd_dir,ret,ret->i_count));
 111         }else{
 112                 umsdos_real_lookup (dir,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN,&ret);
 113                 PRINTK (("emd_dir_lookup "));
 114                 if (ret != NULL){
 115                         PRINTK (("Find --linux "));
 116                         dir->u.umsdos_i.i_emd_dir = ret->i_ino;
 117                 }else if (creat){
 118                         int code;
 119                         PRINTK (("avant create "));
 120                         dir->i_count++;
 121                         code = msdos_create (dir,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN
 122                                 ,S_IFREG|0777,&ret);
 123                         PRINTK (("Creat EMD code %d ret %x ",code,ret));
 124                         if (ret != NULL){
 125                                 dir->u.umsdos_i.i_emd_dir = ret->i_ino;
 126                         }else{
 127                                 printk ("UMSDOS: Can't create EMD file\n");
 128                         }
 129                 }
 130         }
 131         if (ret != NULL){
 132                 /* Disable UMSDOS_notify_change() for EMD file */
 133                 ret->u.umsdos_i.i_emd_owner = 0xffffffff;
 134         }
 135         return ret;
 136 }
 137 
 138 /*
 139         Read an entry from the EMD file.
 140         Support variable length record.
 141         Return -EIO if error, 0 if ok.
 142 */
 143 int umsdos_emd_dir_readentry (
     /* [previous][next][first][last][top][bottom][index][help] */
 144         struct inode *emd_dir,
 145         struct file *filp,
 146         struct umsdos_dirent *entry)
 147 {
 148         int ret = umsdos_emd_dir_read(emd_dir,filp,(char*)entry,UMSDOS_REC_SIZE);
 149         if (ret == 0){
 150                 /* Variable size record. Maybe, we have to read some more */
 151                 int recsize = umsdos_evalrecsize (entry->name_len);
 152                 if (recsize > UMSDOS_REC_SIZE){
 153                         ret = umsdos_emd_dir_read(emd_dir,filp
 154                                 ,((char*)entry)+UMSDOS_REC_SIZE,recsize - UMSDOS_REC_SIZE);
 155                         
 156                 }
 157         }
 158         return ret;
 159 }
 160 /*
 161         Write an entry in the EMD file.
 162         Return 0 if ok, -EIO if some error.
 163 */
 164 int umsdos_writeentry (
     /* [previous][next][first][last][top][bottom][index][help] */
 165         struct inode *dir,
 166         struct inode *emd_dir,
 167         struct umsdos_info *info,
 168         int free_entry)         /* This entry is deleted, so Write all 0's */
 169 {
 170         int ret = 0;
 171         struct file filp;
 172         struct umsdos_dirent *entry = &info->entry;
 173         struct umsdos_dirent entry0;
 174         if (free_entry){
 175                 /* #Specification: EMD file / empty entries
 176                         Unused entry in the EMD file are identify
 177                         by the name_len field equal to 0. However to
 178                         help future extension (or bug correction :-( ),
 179                         empty entries are filled with 0.
 180                 */
 181                 memset (&entry0,0,sizeof(entry0));
 182                 entry = &entry0;
 183         }else if (entry->name_len > 0){
 184                 memset (entry->name+entry->name_len,'\0'
 185                         ,sizeof(entry->name)-entry->name_len);
 186                 /* #Specification: EMD file / spare bytes
 187                         10 bytes are unused in each record of the EMD. They
 188                         are set to 0 all the time. So it will be possible
 189                         to do new stuff and rely on the state of those
 190                         bytes in old EMD file around.
 191                 */
 192                 memset (entry->spare,0,sizeof(entry->spare));
 193         }
 194         filp.f_pos = info->f_pos;
 195         filp.f_reada = 0;
 196         ret = umsdos_emd_dir_write(emd_dir,&filp,(char*)entry,info->recsize);
 197         if (ret != 0){
 198                 printk ("UMSDOS: problem with EMD file. Can't write\n");
 199         }else{
 200                 dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 201                 dir->i_dirt = 1;
 202         }
 203         return ret;
 204 }
 205 
 206 #define CHUNK_SIZE (8*UMSDOS_REC_SIZE)
 207 struct find_buffer{
 208         char buffer[CHUNK_SIZE];
 209         int pos;        /* read offset in buffer */
 210         int size;       /* Current size of buffer */
 211         struct file filp;
 212 };
 213 
 214 /*
 215         Fill the read buffer and take care of the byte remaining inside.
 216         Unread bytes are simply move to the beginning.
 217 
 218         Return -ENOENT if EOF, 0 if ok, a negative error code if any problem.
 219 */
 220 static int umsdos_fillbuf (
     /* [previous][next][first][last][top][bottom][index][help] */
 221         struct inode *inode,
 222         struct find_buffer *buf)
 223 {
 224         int ret = -ENOENT;
 225         int mustmove = buf->size - buf->pos;
 226         int mustread;
 227         int remain;
 228         if (mustmove > 0){
 229                 memcpy (buf->buffer,buf->buffer+buf->pos,mustmove);
 230         }
 231         buf->pos = 0;
 232         mustread = CHUNK_SIZE - mustmove;
 233         remain = inode->i_size - buf->filp.f_pos;
 234         if (remain < mustread) mustread = remain;
 235         if (mustread > 0){
 236                 ret = umsdos_emd_dir_read (inode,&buf->filp,buf->buffer+mustmove
 237                         ,mustread);
 238                 if (ret == 0) buf->size = mustmove + mustread;          
 239         }else if (mustmove){
 240                 buf->size = mustmove;
 241                 ret = 0;
 242         }
 243         return ret;
 244 }
 245 
 246 /*
 247         General search, locate a name in the EMD file or an empty slot to
 248         store it. if info->entry.name_len == 0, search the first empty
 249         slot (of the proper size).
 250 
 251         Caller must do iput on *pt_emd_dir.
 252 
 253         Return 0 if found, -ENOENT if not found, another error code if
 254         other problem.
 255 
 256         So this routine is used to either find an existing entry or to
 257         create a new one, while making sure it is a new one. After you
 258         get -ENOENT, you make sure the entry is stuffed correctly and
 259         call umsdos_writeentry().
 260 
 261         To delete an entry, you find it, zero out the entry (memset)
 262         and call umsdos_writeentry().
 263 
 264         All this to say that umsdos_writeentry must be call after this
 265         function since it rely on the f_pos field of info.
 266 */
 267 static int umsdos_find (
     /* [previous][next][first][last][top][bottom][index][help] */
 268         struct inode *dir,
 269         struct umsdos_info *info,               /* Hold name and name_len */
 270                                                                         /* Will hold the entry found */
 271         struct inode **pt_emd_dir)              /* Will hold the emd_dir inode */
 272                                                                         /* or NULL if not found */
 273 {
 274         /* #Specification: EMD file structure
 275                 The EMD file uses a fairly simple layout. It is made of records
 276                 (UMSDOS_REC_SIZE == 64). When a name can't be written is a single
 277                 record, multiple contiguous record are allocated.
 278         */
 279         int ret = -ENOENT;
 280         struct inode *emd_dir = umsdos_emd_dir_lookup(dir,1);
 281         if (emd_dir != NULL){
 282                 struct umsdos_dirent *entry = &info->entry;
 283                 int recsize = info->recsize;
 284                 struct {
 285                         off_t posok;    /* Position available to store the entry */
 286                         int found;              /* A valid empty position has been found */
 287                         off_t one;              /* One empty position -> maybe <- large enough */
 288                         int onesize;    /* size of empty region starting at one */
 289                 }empty;
 290                 /* Read several entries at a time to speed up the search */
 291                 struct find_buffer buf;
 292                 buf.pos = 0;
 293                 buf.size = 0;
 294                 buf.filp.f_pos = 0;
 295                 buf.filp.f_reada = 1;
 296                 empty.found = 0;
 297                 empty.posok = emd_dir->i_size;
 298                 empty.onesize = 0;
 299                 while (1){
 300                         struct umsdos_dirent *rentry = (struct umsdos_dirent*)
 301                                 (buf.buffer + buf.pos);
 302                         int file_pos = buf.filp.f_pos - buf.size + buf.pos;
 303                         if (buf.pos == buf.size){
 304                                 ret = umsdos_fillbuf (emd_dir,&buf);
 305                                 if (ret < 0){
 306                                         /* Not found, so note where it can be added */
 307                                         info->f_pos = empty.posok;
 308                                         break;
 309                                 }
 310                         }else if (rentry->name_len == 0){
 311                                 /* We are looking for an empty section at least */
 312                                 /* recsize large */
 313                                 if (entry->name_len == 0){
 314                                         info->f_pos = file_pos;
 315                                         ret = 0;
 316                                         break;
 317                                 }else if (!empty.found){
 318                                         if (empty.onesize == 0){
 319                                                 /* This is the first empty record of a section */
 320                                                 empty.one = file_pos;
 321                                         }
 322                                         /* grow the empty section */
 323                                         empty.onesize += UMSDOS_REC_SIZE;
 324                                         if (empty.onesize == recsize){
 325                                                 /* here is a large enough section */
 326                                                 empty.posok = empty.one;
 327                                                 empty.found = 1;
 328                                         }
 329                                 }
 330                                 buf.pos += UMSDOS_REC_SIZE;
 331                         }else{
 332                                 int entry_size = umsdos_evalrecsize(rentry->name_len);
 333                                 if (buf.pos+entry_size > buf.size){
 334                                         ret = umsdos_fillbuf (emd_dir,&buf);
 335                                         if (ret < 0){
 336                                                 /* Not found, so note where it can be added */
 337                                                 info->f_pos = empty.posok;
 338                                                 break;
 339                                         }
 340                                 }else{
 341                                         empty.onesize = 0;      /* Reset the free slot search */
 342                                         if (entry->name_len == rentry->name_len
 343                                                 && memcmp(entry->name,rentry->name,rentry->name_len)
 344                                                         ==0){
 345                                                 info->f_pos = file_pos;
 346                                                 *entry = *rentry;
 347                                                 ret = 0;
 348                                                 break;
 349                                         }else{
 350                                                 buf.pos += entry_size;
 351                                         }
 352                                 }
 353                         }       
 354                 }
 355                 umsdos_manglename(info);
 356         }
 357         *pt_emd_dir = emd_dir;
 358         return ret;
 359 }
 360 /*
 361         Add a new entry in the emd file
 362         Return 0 if ok or a negative error code.
 363         Return -EEXIST if the entry already exist.
 364 
 365         Complete the information missing in info.
 366 */
 367 int umsdos_newentry (
     /* [previous][next][first][last][top][bottom][index][help] */
 368         struct inode *dir,
 369         struct umsdos_info *info)
 370 {
 371         struct inode *emd_dir;
 372         int ret = umsdos_find (dir,info,&emd_dir);
 373         if (ret == 0){
 374                 ret = -EEXIST;
 375         }else if (ret == -ENOENT){
 376                 ret = umsdos_writeentry(dir,emd_dir,info,0);
 377                 PRINTK (("umsdos_newentry EDM ret = %d\n",ret));
 378         }
 379         iput (emd_dir);
 380         return ret;
 381 }
 382 /*
 383         Create a new hidden link.
 384         Return 0 if ok, an error code if not.
 385 */
 386 int umsdos_newhidden (
     /* [previous][next][first][last][top][bottom][index][help] */
 387         struct inode *dir,
 388         struct umsdos_info *info)
 389 {
 390         struct inode *emd_dir;
 391         int ret;
 392         umsdos_parse ("..LINK",6,info);
 393         info->entry.name_len = 0;
 394         ret = umsdos_find (dir,info,&emd_dir);
 395         iput (emd_dir);
 396         if (ret == -ENOENT || ret == 0){
 397                 /* #Specification: hard link / hidden name
 398                         When a hard link is created, the original file is renamed
 399                         to a hidden name. The name is "..LINKNNN" where NNN is a
 400                         number define from the entry offset in the EMD file.
 401                 */
 402                 info->entry.name_len = sprintf (info->entry.name,"..LINK%ld"
 403                         ,info->f_pos);
 404                 ret = 0;
 405         }
 406         return ret;
 407 }
 408 /*
 409         Remove an entry from the emd file
 410         Return 0 if ok, a negative error code otherwise.
 411 
 412         Complete the information missing in info.
 413 */
 414 int umsdos_delentry (
     /* [previous][next][first][last][top][bottom][index][help] */
 415         struct inode *dir,
 416         struct umsdos_info *info,
 417         int isdir)
 418 {
 419         struct inode *emd_dir;
 420         int ret = umsdos_find (dir,info,&emd_dir);
 421         if (ret == 0){
 422                 if (info->entry.name_len != 0){
 423                         if ((isdir != 0) != (S_ISDIR(info->entry.mode) != 0)){
 424                                 if (S_ISDIR(info->entry.mode)){
 425                                         ret = -EISDIR;
 426                                 }else{
 427                                         ret = -ENOTDIR;
 428                                 }
 429                         }else{
 430                                 ret = umsdos_writeentry(dir,emd_dir,info,1);
 431                         }
 432                 }
 433         }
 434         iput(emd_dir);
 435         return ret;
 436 }
 437 
 438 
 439 /*
 440         Verify is a EMD directory is empty.
 441         Return 0 if not empty
 442                    1 if empty
 443                    2 if empty, no EMD file.
 444 */
 445 int umsdos_isempty (struct inode *dir)
     /* [previous][next][first][last][top][bottom][index][help] */
 446 {
 447         int ret = 2;
 448         struct inode *emd_dir = umsdos_emd_dir_lookup(dir,0);
 449         /* If the EMD file does not exist, it is certainly empty :-) */
 450         if (emd_dir != NULL){
 451                 struct file filp;
 452                 /* Find an empty slot */
 453                 filp.f_pos = 0;
 454                 filp.f_reada = 1;
 455                 filp.f_flags = O_RDONLY;
 456                 ret = 1;
 457                 while (filp.f_pos < emd_dir->i_size){
 458                         struct umsdos_dirent entry;
 459                         if (umsdos_emd_dir_readentry(emd_dir,&filp,&entry)!=0){
 460                                 ret = 0;
 461                                 break;
 462                         }else if (entry.name_len != 0){
 463                                 ret = 0;
 464                                 break;
 465                         }       
 466                 }
 467                 iput (emd_dir);
 468         }
 469         return ret;
 470 }
 471 
 472 /*
 473         Locate an entry in a EMD directory.
 474         Return 0 if ok, errcod if not, generally -ENOENT.
 475 */
 476 int umsdos_findentry (
     /* [previous][next][first][last][top][bottom][index][help] */
 477         struct inode *dir,
 478         struct umsdos_info *info,
 479         int expect)             /* 0: anything */
 480                                         /* 1: file */
 481                                         /* 2: directory */
 482 {
 483         struct inode *emd_dir;
 484         int ret = umsdos_find (dir,info,&emd_dir);
 485         if (ret == 0){
 486                 if (expect != 0){
 487                         if (S_ISDIR(info->entry.mode)){
 488                                 if (expect != 2) ret = -EISDIR;
 489                         }else if (expect == 2){
 490                                 ret = -ENOTDIR;
 491                         }
 492                 }
 493         }
 494         iput (emd_dir);
 495         return ret;
 496 }
 497 

/* [previous][next][first][last][top][bottom][index][help] */