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

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