root/fs/umsdos/emd.c

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

DEFINITIONS

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

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