                  NAMESPACE BASED FILE I/O API SPECIFICATION

Abstract

   IIIMF Namespace based file I/O API provides a set of functions which
   have the same semantics as the POSIX file I/O functions.

   This document briefly describes how LEs (Language Engine) can use the
   IIIMF Namespace based FILE I/O APIs.  LEs may access their own
   configuration files, per user data, and such.  If LEs use this set of
   APIs to access these files, input method users can control where those
   files will be located.

   The purpose of these APIs is to provide better I/O implementation for 
   LEs.

General usage

    In general, LEs use this set of APIs in the following order.

    1. in if_OpenIF(), retrieve namespace related functions.
    2. in if_OpenIF(), create a namespace context for the LE.
    3. in if_OpenDesktop() or if_CreateSC(), create a namespace context for
       a user.
    4. while the context is valid, any namespace based file I/O functions
       can be used.
    5. in if_CloseDesktop() or if_DestroySC(), free the namespace context
       for the user.
    6. in if_CloseIF(), free the namespace context for the LE.

    When appropriate, namespace contexts, which will not be used anymore,
    can be freed.

Retrieve namespace related functions

    nsc_get_function() is a member of iml_if_t.  The function can be used to
    retrieve namespace related functions.

    void *(*nsc_get_function) (const char * name);

    nsc_get_function() return a pointer to a function which is specified by
    the name.

    name                function to be returned            function type
    ------------------- ---------------------------------- ----------------
    "_nsc_create"       create a namespace context         iml_nsc_create_t
    "_nsc_free"         free a namespace context           iml_nsc_free_t
    "_nsc_basicfioset"  a set of basic file I/O functions  iml_nsc_bfio_t
    "open"              open() equivalent                  iml_nsc_open_t
    "creat"             creat() equivalent                 iml_nsc_creat_t
    "read"              read() equivalent                  iml_nsc_read_t
    "write"             write() equivalent                 iml_nsc_write_t
    "stat"              stat() equivalent                  iml_nsc_stat_t
    "lstat"             lstat() equivalent                 iml_nsc_lstat_t
    "fstat"             fstat() equivalent                 iml_nsc_fstat_t
    "fcntl"             fcntl() equivalent                 iml_nsc_fcntl_t
    "close"             close() equivalent                 iml_nsc_close_t
    "mkdir"             mkdir() equivalent                 iml_nsc_mkdir_t
    "rmdir"             rmdir() equivalent                 iml_nsc_rmdir_t
    "rename"            rename() equivalent                iml_nsc_rename_t
    "symlink"           symlink() equivalent               iml_nsc_symlink_t
    "truncate"          truncate() equivalent              iml_nsc_truncate_t
    "ftruncate"         ftruncate() equivalent             iml_nsc_ftruncate_t
    "unlink"            unlink() equivalent                iml_nsc_unlink_t
    "opendir"           opendir() equivalent               iml_nsc_opendir_t
    "readdir"           readdir() equivalent               iml_nsc_readdir_t
    "closedir"          closedir() equivalent              iml_nsc_closedir_t

    If the specified function is not supported, nsc_get_function() returns
    NULL.  LEs need to verify the returned value before calling functions.

    Example:

    1. Using nsc_get_function() to retrieve functions for creating namespace 
       context and to free the namespace context.

       le_nsc_create = (iml_nsc_create_t) (If->nsc_get_function("_nsc_create");
       le_nsc_free = (iml_nsc_free_t) (If->nsc_get_function("_nsc_free");

    2. Using nsc_get_function() to retrieve namespace I/O functions by passing 
       function names.

       le_open = (iml_nsc_open_t) (If->nsc_get_function("open");
       le_creat = (iml_nsc_creat_t) (If->nsc_get_function("creat");
       le_close = (iml_nsc_close_t) (If->nsc_get_function("close");
       le_read = (iml_nsc_read_t) (If->nsc_get_function("read");
       le_write = (iml_nsc_write_t) (If->nsc_get_function("write");
       le_stat = (iml_nsc_stat_t) (If->nsc_get_function("stat");
       le_lstat = (iml_nsc_lstat_t) (If->nsc_get_function("lstat");
       le_fstat = (iml_nsc_fstat_t) (If->nsc_get_function("fstat");
       le_fcntl = (iml_nsc_fcntl_t) (If->nsc_get_function("fcntl");
       le_mkdir = (iml_nsc_mkdir_t) (If->nsc_get_function("mkdir");
       le_rmdir = (iml_nsc_rmdir_t) (If->nsc_get_function("rmdir");
       le_rename = (iml_nsc_rename_t) (If->nsc_get_function("rename");
       le_symlink = (iml_nsc_symlink_t) (If->nsc_get_function("symlink");
       le_truncate = (iml_nsc_truncate_t) (If->nsc_get_function("truncate");
       le_ftruncate = (iml_nsc_ftruncate_t) (If->nsc_get_function("ftruncate");
       le_unlink = (iml_nsc_unlink_t) (If->nsc_get_function("unlink");
       le_opendir = (iml_nsc_opendir_t) (If->nsc_get_function("opendir");
       le_readdir = (iml_nsc_readdir_t) (If->nsc_get_function("readdir");
       le_closedir = (iml_nsc_closedir_t) (If->nsc_get_function("closedir");

    3. Using nsc_get_function() to retrieve a set of stable I/O functions by 
       passing the string "_nsc_basicfioset"

       iml_nsc_basic_fioset_t *bfio;

       bfio = (iml_nsc_basic_fioset_t *) 
                                 (If->nsc_get_function("_nsc_basicfioset"));

Create a namespace context for the LE

    If an LE access files which is not related to a specific user, the LE
    can create a namespace context and use it to access the files.

    Example:

    iml_nsc_t * nsc;
    iml_if_t * If;
    int         fd;
    int         rval;
    char        buf[1024];

    nsc = le_nsc_create("sampleLE", IML_NSC_TYPE_LE, If);
    fd = le_open(nsc, "/etc/iiim/le/sampleLE/sampleLE.cfg", O_RDONLY);
    rval = le_read(nsc, fd, buf, sizeof(buf));
    /* do something */
    le_close(nsc, fd);

    Note: 

    1. IIIMServer reads the Namespace mapping rules created by the User
       and then redirects the I/O operations.

       Sample namespace mapping rules are defined in the last section.

    2. The namespace based file I/O calls such as le_open(), le_creat(),
       le_opendir() ..etc, returns a namespace identifier which is the 
       file descriptor (or) the directory stream pointer equivalent. This
       namespace identifier should be passed to other I/O calls such as 
       le_read(), le_write(), le_close(), le_stat(), le_readdir(), 
       le_closedir(), le_fcntl() ..etc.

Create a namespace context for an user

    If LE needs access to files related to a specific user, then LE can
    create a namespace context for the user, and use it to access the files.

    Example:

    iml_nsc_t *     nsc;
    iml_desktop_t * desktop;
    int             fd;
    int             rval;
    char            buf[1024];

    nsc = le_nsc_create("sampleLE", IML_NSC_TYPE_DESKTOP, desktop);
    fd = le_open(nsc, "/var/lib/iiim/le/sampleLE/userA/config", O_RDONLY);
    rval = le_read(nsc, fd, buf, sizeof (buf));
    /* do something */
    le_close(nsc, fd);

    Note: 

    IIIMServer reads the Namespace mapping rules created by the User
    and then redirects the I/O operations.

    Sample namespace mapping rules are defined in the last section.

Free the namespace context for the user

    If an LE does not need a namespace context for a specific user anymore,
    the LE can free the namespace context. 

    Example:

    iml_nsc_t * nsc;

    le_nsc_free(nsc);


Free the namespace context for the LE

    If an LE does not need a namespace context which is not related to
    specific user anymore, the LE can free the namespace context.

    Example:

    iml_nsc_t * nsc;

    le_nsc_free(nsc);


Porting the standard POSIX I/O API based code to Namespace based I/O APIs

     Assume the following example in sample LE, which loads the file
     "/var/lib/iiim/le/sampleLE/sampleLE.cfg"

     int
     open_config_file() {
       char file_name[512];
       int fd, nread;
       struct stat buf;

       snprintf(file_name, sizeof(file_name), "%s", "/var/lib/iiim/le/sampleLE/sampleLE.cfg");
       fd = open(file_name, O_RDONLY);
       if (fd < 0) {
         printf("Error in opening the file: errno [%d]\n", errno);
         return -1;
       } 
       if (stat(file_name, &buf) < 0) {
         printf("Error in stat: errno [%d]\n", errno);
         close(fd);
         return -1;
       } 
       file_buf = (char *) calloc(buf.st_size, sizeof(char)); 
       nread = read(fd, file_buf, buf.st_size);
       if (nread <= 0) {
         printf("Error in read: errno [%d]\n", errno);
         close(fd);
         return -1;
       }

       /* 
          process the file_buf that was read. 
       */

       close(fd);

       return 1;
     }

     There are two ways to modify the code above to make use of the namespace 
     based I/O APIs.

     I) Using nsc_get_function() to retrieve I/O function by passing function names.

        /* Global definition */
        iml_nsc_t *nsc;
        iml_nsc_create_t le_nsc_create;
        iml_nsc_free_t le_nsc_free;
        iml_nsc_open_t le_open;
        iml_nsc_stat_t le_stat;
        iml_nsc_read_t le_read;
        iml_nsc_close_t le_close;

        void 
        get_nsc_methods {
          iml_desktop_t *desktop
        }
        {
          iml_if_t *If = desktop->If;

          le_nsc_create = (iml_nsc_create_t) (If->nsc_get_function("_nsc_create");
          le_nsc_free = (iml_nsc_free_t) (If->nsc_get_function("_nsc_free");

          le_open = (iml_nsc_open_t) (If->nsc_get_function(nsc, "open"));
          le_stat = (iml_nsc_stat_t) (If->nsc_get_function(nsc, "stat"));
          le_read = (iml_nsc_read_t) (If->nsc_get_function(nsc, "read"));
          le_close = (iml_nsc_close_t) (If->nsc_get_function(nsc, "close"));
        }

        int
        open_config_file(
          iml_desktop_t *desktop
        ) 
        {
          int fd, nread;
          struct stat buf;
          iml_nsc *nsc;
   
          /* 
            The nsc function binding can be global. If global, the initialization
            can be done just only once.
          */
          get_nsc_method(desktop);

          nsc = le_nsc_create("sampleLE", IML_NSC_TYPE_DESKTOP, desktop);

          snprintf(file_name, sizeof(file_name), "%s", "/var/lib/iiim/le/sampleLE/sampleLE.cfg");

          if (le_open) {
            fd = le_open(nsc, file_name, O_RDONLY);
            if (fd < 0) {
              printf("Error in opening the file: errno [%d]\n", errno);
              return -1;
            } 
  
            if (le_stat) {
              if (le_stat(nsc, file_name, &buf) < 0) {
                printf("Error in stat: errno [%d]\n", errno);
                le_close(nsc, fd);
                return -1;
              } 
              if (le_read) {
                file_buf = (char *) calloc(buf.st_size, sizeof(char)); 
                nread = le_read(nsc, fd, file_buf, buf.st_size);
                if (nread <= 0) {
                  printf("Error in read: errno [%d]\n", errno);
                  le_close(nsc, fd);
                  return -1;
                }
              }
       
              /* 
                 process the file_buf that was read. 
              */
     
              le_close(nsc, fd);
            }
          }
   
          return 1;
       }

     II) Using nsc_get_function() to retrieve a set of stable I/O functions by 
         passing the string "_nsc_basicfioset".

         int
         open_config_file(
           iml_desktop_t *desktop
         ) 
         {
           int fd, nread;
           struct stat buf;
           iml_nsc *nsc;
           iml_nsc_basic_fioset_t *bfio;
           iml_if_t *If = desktop->If;
    
           nsc = le_nsc_create("sampleLE", IML_NSC_TYPE_DESKTOP, desktop);

           if (!nsc) {
             printf("Namespace context is Null\n");
             return -1;
           }

           bfio = (iml_nsc_basic_fioset_t *) (If->nsc_get_function("_nsc_basicfioset"));

           if (!bfio) {
             printf("Error in getting the iml_nsc_basic_fioset_t reference \n");
             return -1;
           }

           snprintf(file_name, sizeof(file_name), "%s", "/var/lib/iiim/le/sampleLE/sampleLE.cfg");
           fd = bfio->open(nsc, file_name, O_RDONLY);
           if (fd < 0) {
             printf("Error in opening the file: errno [%d]\n", errno);
             return -1;
           } 
           if (bfio->stat(nsc, file_name, &buf) < 0) {
             printf("Error in stat: errno [%d]\n", errno);
             bfio->close(nsc, fd);
             return -1;
           } 
           file_buf = (char *) calloc(buf.st_size, sizeof(char)); 
           nread = bfio->read(nsc, fd, file_buf, buf.st_size);
           if (nread <= 0) {
             printf("Error in read: errno [%d]\n", errno);
             bfio->close(nsc, fd);
             return -1;
           }
    
           /* 
             process the file_buf that was read. 
           */
    
           bfio->close(nsc, fd);
    
           return 1;
         }

Namespace mapping rules

    Users can create mapping rules that are parsed by IIIM server.  When LEs
    perform I/O with this set of APIs, IIIM server uses this rules to
    redirect I/O operations.

    Files can be divided into the following categories.

    category                       default location
    ------------------------------ ------------------------------
    user                           ${HOME}/.iiim or /var/lib/iiim
    system read only               /usr/lib/iiim/le
    system read only (alternate)   /usr/lib
    system configuration           /etc/iiim
    system volatile                /var/lib/iiim

    The design of namespace mapping rules is TBD.
