summaryrefslogblamecommitdiffstats
path: root/cpukit/libfs/src/dosfs/msdos_create.c
blob: bbdf1376008f200c71ecb816fc67e47ee1f730dc (plain) (tree)
1
2
3
4
5
6
7
8
9







                                                          
                                        









                   
                  











                               







                                                                             


                                                                            
                                                                               





                                                                     

                                                                     


                                                                          
  
   
   





                                                               
 


















                                                                        
                                                                 
 


                                                                

                             
                                                       
 




                                         


                                                          
 
                                        
                                                           
 

                                                          



                                                   
                                                        
                                  
         


                                          
                                                               


                                                                       

                                                 
                                                                         

                                       
                    
       


                                



                                                                                 
 
        

                                              
                                                                            
 
                                                
                                                   
                                               
                                                  


                                         
                                                        


                                        
                                                              

                                
                                                          
     
 

                                                                        
                                                         
       

                                                              

                      


                                                                             




                                            
                                                                    


                        
          


                                                                              


                                                  



                                                                      
           
                                                  
                                                  
                                                     

                                                                      
                               
                                                                            
                               
 
                                                 


                                                                           
                                                                         

                                                  






                                                                             
                                                                     
                                                                         
                                                                    
                                                                               




                                                                            

                                                     

                                                                   
                                                    




                       
 




                                                              
                                                                  
                                                             
                                                                          

                               
                                                             
                                                               
                                                                




                       
 
                                                                
                                                                       
                        

                       







                                                     

                                                                         

              





                                                                           
  


                                                                            
                                                         
                                
  













                                                                         
   
                                                         

                                                          





                                                               
                             
                           
 


                                              
                                                            
                                                         





                                                                  
                                                  






                                                                
                                                  





                                                 

                                                                   







                                                                    
                                                                 







                                               
/*
 * Routine to create a new MSDOS filesystem node
 *
 * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
 * Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru>
 *
 * The license and distribution terms for this file may be
 * found in the file LICENSE in this distribution or at
 * http://www.rtems.com/license/LICENSE.
 *
 * @(#) $Id$
 *
 */
#if HAVE_CONFIG_H
#include "config.h"
#endif

#include <errno.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rtems/libio_.h>
#include <time.h>

#include "fat.h"
#include "fat_fat_operations.h"
#include "fat_file.h"

#include "msdos.h"

/* msdos_creat_node --
 *     Create a new node. Determine if the name is a long name. If long we to
 *     scan the directory to create a short entry.
 *
 *



 *     If a new node is file, FAT 32 Bytes Directory
 *     Entry Structure is initialized, free space is found in parent
 *     directory and structure is written to the disk. In case of directory,
 *     all above steps present and also new cluster is allocated for a
 *     new directory and dot and dotdot nodes are created in alloceted cluster.
 *
 * PARAMETERS:
 *     parent_loc - parent (directory we are going to create node in)
 *     type       - new node type (file or directory)
 *     name       - new node name
 *     mode       - mode
 *     link_info  - fs_info of existing node for a pseudo "hard-link"
 *                  (see msdos_file.c, msdos_link for documentation)
 *
 * RETURNS:
 *     RC_OK on success, or -1 if error occured (errno set appropriately).
 *
 */
int
msdos_creat_node(rtems_filesystem_location_info_t  *parent_loc,
                 msdos_node_type_t                  type,
                 const char                        *name,
                 int                                name_len,
                 mode_t                             mode,
                 const fat_file_fd_t               *link_fd)
{
    int               rc = RC_OK;
    ssize_t           ret = 0;
    msdos_fs_info_t  *fs_info = parent_loc->mt_entry->fs_info;
    fat_file_fd_t    *parent_fat_fd = parent_loc->node_access;
    fat_file_fd_t    *fat_fd = NULL;
    time_t            time_ret = 0;
    uint16_t          time_val = 0;
    uint16_t          date = 0;
    fat_dir_pos_t     dir_pos;
    msdos_name_type_t name_type;
    char              short_node[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE];
    char              dot_dotdot[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE * 2];
    char              link_node[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE];
    uint32_t          sec = 0;
    uint32_t          byte = 0;

    fat_dir_pos_init(&dir_pos);
    
    memset(short_node, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
    memset(dot_dotdot, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE * 2);

    name_type = msdos_long_to_short (name, name_len,
                                     MSDOS_DIR_NAME(short_node),
                                     MSDOS_NAME_MAX);

    /* fill reserved field */
    *MSDOS_DIR_NT_RES(short_node) = MSDOS_RES_NT_VALUE;

    /* set up last write date and time */
    time_ret = time(NULL);
    if ( time_ret == -1 )
        return -1;

    msdos_date_unix2dos(time_ret, &date, &time_val);
    *MSDOS_DIR_WRITE_TIME(short_node) = CT_LE_W(time_val);
    *MSDOS_DIR_WRITE_DATE(short_node) = CT_LE_W(date);

    /* initialize directory/file size */
    *MSDOS_DIR_FILE_SIZE(short_node) = MSDOS_INIT_DIR_SIZE;

    if (type == MSDOS_DIRECTORY) {
      *MSDOS_DIR_ATTR(short_node) |= MSDOS_ATTR_DIRECTORY;
    }
    else if (type == MSDOS_HARD_LINK) {
      /*
       * when we establish a (temporary) hard link,
       * we must copy some information from the original
       * node to the newly created
       */
      /*
       * read the original directory entry
       */
      sec = fat_cluster_num_to_sector_num(parent_loc->mt_entry,
                                          link_fd->dir_pos.sname.cln);
      sec += (link_fd->dir_pos.sname.ofs >> fs_info->fat.vol.sec_log2);
      byte = (link_fd->dir_pos.sname.ofs & (fs_info->fat.vol.bps - 1));

      ret = _fat_block_read(parent_loc->mt_entry,
                            sec, byte, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE,
                            link_node);
      if (ret < 0) {
          return -1;
      }
      /*
       * copy various attributes
       */
      *MSDOS_DIR_ATTR(short_node)          =*MSDOS_DIR_ATTR(link_node);
      *MSDOS_DIR_CRT_TIME_TENTH(short_node)=*MSDOS_DIR_CRT_TIME_TENTH(link_node);
      *MSDOS_DIR_CRT_TIME(short_node)      =*MSDOS_DIR_CRT_TIME(link_node);
      *MSDOS_DIR_CRT_DATE(short_node)      =*MSDOS_DIR_CRT_DATE(link_node);

      /*
       * copy/set "file size", "first cluster"
       */
      *MSDOS_DIR_FILE_SIZE(short_node)     =*MSDOS_DIR_FILE_SIZE(link_node);

      *MSDOS_DIR_FIRST_CLUSTER_LOW(short_node) =
           *MSDOS_DIR_FIRST_CLUSTER_LOW(link_node);
      *MSDOS_DIR_FIRST_CLUSTER_HI(short_node) =
           *MSDOS_DIR_FIRST_CLUSTER_HI(link_node);
      /*
       * set "archive bit" due to changes
       */
      *MSDOS_DIR_ATTR(short_node) |= MSDOS_ATTR_ARCHIVE;
      /*
       * set "last access" date to today
       */
      *MSDOS_DIR_LAST_ACCESS_DATE(short_node) = CT_LE_W(date);
    }
    else { /* regular file... */
        *MSDOS_DIR_ATTR(short_node) |= MSDOS_ATTR_ARCHIVE;
    }

    /*
     * find free space in the parent directory and write new initialized
     * FAT 32 Bytes Directory Entry Structure to the disk
     */
    rc = msdos_get_name_node(parent_loc, true, name, name_len,
                             name_type, &dir_pos, short_node);
    if ( rc != RC_OK )
        return rc;

    /*
     * if we create a new file we are done, if directory there are more steps
     * to do
     */
    if (type == MSDOS_DIRECTORY)
    {
        /* open new directory as fat-file */
        rc = fat_file_open(parent_loc->mt_entry, &dir_pos, &fat_fd);
        if (rc != RC_OK)
            goto err;

        /*
         * we opened fat-file for node we just created, so initialize fat-file
         * descritor
         */
        fat_fd->fat_file_size = 0;
        fat_fd->fat_file_type = FAT_DIRECTORY;
        fat_fd->size_limit = MSDOS_MAX_DIR_LENGHT;

        /*
         * dot and dotdot entries are identical to new node except the
         * names
         */
        memcpy(DOT_NODE_P(dot_dotdot), short_node,
               MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
        memcpy(DOTDOT_NODE_P(dot_dotdot), short_node,
               MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
        memcpy(MSDOS_DIR_NAME(DOT_NODE_P(dot_dotdot)), MSDOS_DOT_NAME,
               MSDOS_NAME_MAX);
        memcpy(MSDOS_DIR_NAME(DOTDOT_NODE_P(dot_dotdot)), MSDOS_DOTDOT_NAME,
               MSDOS_NAME_MAX);

        /* set up cluster num for dotdot entry */
        /*
         * here we can ommit FAT32 condition because for all FAT types dirs
         * right under root dir should contain 0 in dotdot entry but for
         * FAT12/16 parent_fat_fd->cluster_num always contains such value
         */
        if ((FAT_FD_OF_ROOT_DIR(parent_fat_fd)) &&
            (fs_info->fat.vol.type & FAT_FAT32))
        {
            *MSDOS_DIR_FIRST_CLUSTER_LOW(DOTDOT_NODE_P(dot_dotdot)) = 0x0000;
            *MSDOS_DIR_FIRST_CLUSTER_HI(DOTDOT_NODE_P(dot_dotdot)) = 0x0000;
        }
        else
        {
            *MSDOS_DIR_FIRST_CLUSTER_LOW(DOTDOT_NODE_P(dot_dotdot)) =
                CT_LE_W((uint16_t  )((parent_fat_fd->cln) & 0x0000FFFF));
            *MSDOS_DIR_FIRST_CLUSTER_HI(DOTDOT_NODE_P(dot_dotdot)) =
                CT_LE_W((uint16_t  )(((parent_fat_fd->cln) & 0xFFFF0000)>>16));
        }

        /*
         * write dot and dotdot entries to new fat-file: currently fat-file
         * correspondes to a new node is zero length, so it will be extended
         * by one cluster and entries will be written
         */
        ret = fat_file_write(parent_loc->mt_entry, fat_fd, 0,
                             MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE * 2,
                             (uint8_t *)dot_dotdot);
        if (ret < 0)
        {
            rc = -1;
            goto error;
        }

        /* increment fat-file size by cluster size */
        fat_fd->fat_file_size += fs_info->fat.vol.bpc;

        /* set up cluster num for dot entry */
        *MSDOS_DIR_FIRST_CLUSTER_LOW(DOT_NODE_P(dot_dotdot)) =
                CT_LE_W((uint16_t  )((fat_fd->cln) & 0x0000FFFF));
        *MSDOS_DIR_FIRST_CLUSTER_HI(DOT_NODE_P(dot_dotdot)) =
                CT_LE_W((uint16_t  )(((fat_fd->cln) & 0xFFFF0000) >> 16));

        /* rewrite dot entry */
        ret = fat_file_write(parent_loc->mt_entry, fat_fd, 0,
                             MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE,
                             (uint8_t *)DOT_NODE_P(dot_dotdot));
        if (ret < 0)
        {
            rc = -1;
            goto error;
        }

        /* write first cluster num of a new directory to disk */
        rc = msdos_set_first_cluster_num(parent_loc->mt_entry, fat_fd);
        if (rc != RC_OK)
            goto error;

        fat_file_close(parent_loc->mt_entry, fat_fd);
    }
    return RC_OK;

error:
    fat_file_close(parent_loc->mt_entry, fat_fd);

err:
    /* mark the used 32bytes structure on the disk as free */
    msdos_set_first_char4file_name(parent_loc->mt_entry, &dir_pos, 0xE5);
    return rc;
}

/* msdos_file_link --
 *     Replacement for a file "link" operation.
 *     MSDOS FAT FS does not support links, but this call is needed to
 *     allow "rename" operations. The current NEWLIB rename performs a link
 *     from the old to the new name and then deletes the old filename.
 *
 *     This pseudo-"link" operation will create a new directory entry,
 *     copy the file size and cluster information from the "old"
 *     to the "new" directory entry and then clear the file size and cluster
 *     info from the "old" filename, leaving this file as
 *     a valid, but empty entry.
 *
 *     When this "link" call is part of a "rename" sequence, the "old"
 *     entry will be deleted in a subsequent "rmnod" call
 *
 *     This function has been implemented by Thomas Doerfler,
 *     <Thomas.Doerfler@imd-systems.de>
 *
 * PARAMETERS:
 *     to_loc     - node description for "existing" node
 *     par_loc    - node description for "new" node
 *     token      - name of new node
 *
 * RETURNS:
 *     RC_OK on success, or -1 if error occured (errno set appropriately)
 */
int
msdos_file_link(rtems_filesystem_location_info_t *to_loc,
                rtems_filesystem_location_info_t *par_loc,
                const char                       *name
)
{
    int                rc = RC_OK;
    rtems_status_code  sc = RTEMS_SUCCESSFUL;
    msdos_fs_info_t   *fs_info     = to_loc->mt_entry->fs_info;
    fat_file_fd_t     *to_fat_fd   = to_loc->node_access;
    const char        *token;
    int                len;

    /*
     * check spelling and format new node name
     */
    if (MSDOS_NAME != msdos_get_token(name, &token, &len)) {
      rtems_set_errno_and_return_minus_one(ENAMETOOLONG);
    }
    /*
     * verify, that the existing node can be linked to
     * check that nodes are in same FS/volume?
     */
    if (to_loc->mt_entry->fs_info != par_loc->mt_entry->fs_info) {
      rtems_set_errno_and_return_minus_one(EXDEV);
    }
    /*
     * lock volume
     */
    sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT,
                                MSDOS_VOLUME_SEMAPHORE_TIMEOUT);
    if (sc != RTEMS_SUCCESSFUL)
        rtems_set_errno_and_return_minus_one(EIO);


    /*
     * create new directory entry as "hard link",
     * copying relevant info from existing file
     */
    rc = msdos_creat_node(par_loc,MSDOS_HARD_LINK,name,len,S_IFREG,
                          to_loc->node_access);
    /*
     * set file size and first cluster number of old entry to 0
     */
    if (rc == RC_OK) {
      to_fat_fd->fat_file_size = 0;
      to_fat_fd->cln           = FAT_EOF;
      rc = msdos_set_first_cluster_num(to_loc->mt_entry, to_fat_fd);
      if (rc == RC_OK) {
          rc = msdos_set_file_size(par_loc->mt_entry, to_fat_fd);
      }
    }
    /*
     * FIXME: check error/abort handling
     */
    rtems_semaphore_release(fs_info->vol_sema);
    return rc;
}