plover/fileio.c
author J. Ali Harlow <ali@juiblex.co.uk>
Thu Jun 16 18:00:21 2016 +0100 (2016-06-16)
changeset 43 6b3034a884dc
parent 41 bd50a4b7ab68
permissions -rw-r--r--
Add test for non-file URI support
     1 /*
     2  * Copyright (C) 2016  J. Ali Harlow <ali@juiblex.co.uk>
     3  *
     4  * This program is free software; you can redistribute it and/or modify
     5  * it under the terms of the GNU General Public License as published by
     6  * the Free Software Foundation; either version 2 of the License, or
     7  * (at your option) any later version.
     8  *
     9  * This program is distributed in the hope that it will be useful,
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  * GNU General Public License for more details.
    13  *
    14  * You should have received a copy of the GNU General Public License along
    15  * with this program; if not, write to the Free Software Foundation, Inc.,
    16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    17  */
    18 
    19 #include <stdlib.h>
    20 #include <string.h>
    21 #include <errno.h>
    22 #include <razor.h>
    23 #include <glib.h>
    24 #include <gio/gio.h>
    25 #include "config.h"
    26 #include "plover/plover.h"
    27 
    28 static GList *open_razor_files;
    29 
    30 static gboolean has_valid_scheme(const char *uri)
    31 {
    32     /*
    33      * RFC 2396 defines valid schemes as:
    34      * scheme = alpha *( alpha | digit | "+" | "-" | "." )
    35      */
    36     const char *s;
    37     if (!g_ascii_isalpha(*uri))
    38 	return FALSE;
    39     for(s=uri+1;;s++)
    40 	if (*s==':')
    41 	    return TRUE;
    42 	else if (!g_ascii_isalnum(*s) && *s!='+' && *s!='-' && *s!='.')
    43 	    return FALSE;
    44 }
    45 
    46 static GFile *file_for_uri(const char *uri)
    47 {
    48     GFile *file;
    49     if (!has_valid_scheme(uri))
    50     {
    51 	g_warning("%s: Not a valid URI",uri);
    52 	file=g_file_new_for_path(uri);
    53     }
    54     else
    55     {
    56 	if (strstr(uri+1,"file:/"))
    57 	    g_warning("%s: Implausible URI",uri);
    58 	file=g_file_new_for_uri(uri);
    59     }
    60     return file;
    61 }
    62 
    63 int file_mkdir(const char *uri,mode_t mode,struct razor_error **error)
    64 {
    65     GFile *file;
    66     GFileInfo *info;
    67     gchar *path;
    68     int retval;
    69     GError *err=NULL;
    70     g_message("file_mkdir(%s)",uri);
    71     file=file_for_uri(uri);
    72     path=g_file_get_path(file);
    73     if (path)
    74     {
    75 	retval=razor_file_default_mkdir(path,mode,error);
    76 	g_free(path);
    77     }
    78     else if (!g_file_make_directory(file,NULL,&err))
    79     {
    80 	retval=-1;
    81 	if (g_error_matches(err,G_IO_ERROR,G_IO_ERROR_EXISTS))
    82 	{
    83 	    info=g_file_query_info(file,G_FILE_ATTRIBUTE_STANDARD_TYPE,
    84 	      G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,NULL,NULL);
    85 	    if (info)
    86 	    {
    87 		g_clear_error(&err);
    88 		if (g_file_info_get_file_type(info)==G_FILE_TYPE_DIRECTORY)
    89 		    retval=0;
    90 		else
    91 		    razor_set_error(error,RAZOR_POSIX_ERROR,EEXIST,uri,
    92 		      "Not a directory");
    93 		g_object_unref(info);
    94 	    }
    95 	}
    96 	if (err)
    97 	    plover_propagate_g_error(error,err);
    98     }
    99     else
   100 	retval=0;
   101     g_object_unref(file);
   102     return retval;
   103 }
   104 
   105 int file_unlink(const char *uri,struct razor_error **error)
   106 {
   107     GFile *file;
   108     gchar *path;
   109     int retval;
   110     GError *err=NULL;
   111     g_message("file_unlink(%s)",uri);
   112     file=file_for_uri(uri);
   113     path=g_file_get_path(file);
   114     if (path)
   115     {
   116 	retval=razor_file_default_unlink(path,error);
   117 	g_free(path);
   118     }
   119     else if (!g_file_delete(file,NULL,&err))
   120     {
   121 	plover_propagate_g_error(error,err);
   122 	retval=-1;
   123     }
   124     else
   125 	retval=0;
   126     g_object_unref(file);
   127     return retval;
   128 }
   129 
   130 int file_open(const char *uri,int flags,mode_t mode,struct razor_error **error)
   131 {
   132     GFile *file;
   133     gchar *path;
   134     int retval;
   135     GError *err=NULL;
   136     g_message("file_open(%s)",uri);
   137     file=file_for_uri(uri);
   138     path=g_file_get_path(file);
   139     if (path)
   140     {
   141 	retval=razor_file_default_open(path,flags,mode,error);
   142 	g_free(path);
   143     }
   144     else
   145     {
   146 	razor_set_error(error,RAZOR_GENERAL_ERROR,RAZOR_GENERAL_ERROR_FAILED,
   147 	  uri,"File is not local");
   148 	retval=-1;
   149     }
   150     g_object_unref(file);
   151     return retval;
   152 }
   153 
   154 int file_move(const char *src_uri,const char *dst_uri,
   155   struct razor_error **error)
   156 {
   157     GFile *src,*dst;
   158     gchar *src_path,*dst_path;
   159     int retval;
   160     GError *err=NULL;
   161     g_message("file_move(%s,%s)",src_uri,dst_uri);
   162     src=file_for_uri(src_uri);
   163     dst=file_for_uri(dst_uri);
   164     src_path=g_file_get_path(src);
   165     dst_path=g_file_get_path(dst);
   166     if (src_path && dst_path)
   167 	retval=razor_file_default_move(src_path,dst_path,error);
   168     else if (!g_file_move(src,dst,G_FILE_COPY_OVERWRITE,NULL,NULL,NULL,&err))
   169     {
   170 	plover_propagate_g_error(error,err);
   171 	retval=-1;
   172     }
   173     else
   174 	retval=0;
   175     g_free(src_path);
   176     g_free(dst_path);
   177     g_object_unref(src);
   178     g_object_unref(dst);
   179     return retval;
   180 }
   181 
   182 static void *file_get_contents(const char *uri,size_t *length,int private,
   183   struct razor_error **error)
   184 {
   185     GFile *file;
   186     gchar *path;
   187     void *addr;
   188     char *contents;
   189     gsize len;
   190     GError *err=NULL;
   191     g_message("file_get_contents(%s)",uri);
   192     file=file_for_uri(uri);
   193     path=g_file_get_path(file);
   194     if (path)
   195     {
   196 	g_object_unref(file);
   197 	addr=razor_file_default_get_contents(path,length,private,error);
   198 	if (addr)
   199 	    open_razor_files=g_list_prepend(open_razor_files,addr);
   200 	g_free(path);
   201     }
   202     else if (!g_file_load_contents(file,NULL,&contents,&len,NULL,&err))
   203     {
   204 	plover_propagate_g_error(error,err);
   205 	g_object_unref(file);
   206 	addr=NULL;
   207     }
   208     else
   209     {
   210 	g_object_unref(file);
   211 	addr=contents;
   212 	if (length)
   213 	    *length=len;
   214     }
   215     return addr;
   216 }
   217 
   218 static int file_free_contents(void *addr,size_t length)
   219 {
   220     int retval;
   221     GList *lnk;
   222     g_message("file_free_contents(%p)",addr);
   223     lnk=g_list_find(open_razor_files,addr);
   224     if (lnk)
   225     {
   226 	open_razor_files=g_list_delete_link(open_razor_files,lnk);
   227 	retval=razor_file_default_free_contents(addr,length);
   228     }
   229     else
   230     {
   231 	g_free(addr);
   232 	retval=0;
   233     }
   234     return retval;
   235 }
   236 
   237 int file_is_directory(const char *uri,struct razor_error **error)
   238 {
   239     GFile *file;
   240     GFileInfo *info;
   241     gchar *path;
   242     int retval;
   243     GError *err=NULL;
   244     g_message("file_is_directory(%s)",uri);
   245     file=file_for_uri(uri);
   246     path=g_file_get_path(file);
   247     if (path)
   248     {
   249 	retval=razor_file_default_is_directory(path,error);
   250 	g_free(path);
   251     }
   252     else
   253     {
   254 	info=g_file_query_info(file,G_FILE_ATTRIBUTE_STANDARD_TYPE,
   255 	  G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,NULL,&err);
   256 	if (info)
   257 	{
   258 	    if (g_file_info_get_file_type(info)==G_FILE_TYPE_DIRECTORY)
   259 		retval=1;
   260 	    else
   261 		retval=0;
   262 	    g_object_unref(info);
   263 	}
   264 	else
   265 	{
   266 	    retval=-1;
   267 	    plover_propagate_g_error(error,err);
   268 	}
   269     }
   270     g_object_unref(file);
   271     return retval;
   272 }
   273 
   274 char *file_mkdtemp_near(const char *uri,const char *template,
   275   struct razor_error **error)
   276 {
   277     GFile *file;
   278     gchar *path,*tmpuri;
   279     char *tmppath,*retval;
   280     GError *err=NULL;
   281     g_message("file_mkdtemp_near(%s)",uri);
   282     file=file_for_uri(uri);
   283     path=g_file_get_path(file);
   284     g_object_unref(file);
   285     if (path)
   286     {
   287 	tmppath=razor_file_default_mkdtemp_near(path,template,error);
   288 	g_free(path);
   289 	if (tmppath)
   290 	{
   291 	    file=g_file_new_for_path(tmppath);
   292 	    tmpuri=g_file_get_uri(file);
   293 	    g_object_unref(file);
   294 	    retval=strdup(tmpuri);
   295 	    g_free(tmpuri);
   296 	}
   297 	else
   298 	    retval=NULL;
   299     }
   300     else
   301     {
   302 	razor_set_error(error,RAZOR_GENERAL_ERROR,RAZOR_GENERAL_ERROR_FAILED,
   303 	  uri,"File is not local");
   304 	retval=NULL;
   305     }
   306     return retval;
   307 }
   308 
   309 struct file_dir {
   310     void *razor_file_default;
   311     GFileEnumerator *enumerator;
   312 };
   313 
   314 void *file_opendir(const char *uri,struct razor_error **error)
   315 {
   316     GFile *file;
   317     gchar *path;
   318     struct file_dir *dir;
   319     GError *err=NULL;
   320     g_message("file_opendir(%s)",uri);
   321     dir=g_new0(struct file_dir,1);
   322     file=file_for_uri(uri);
   323     path=g_file_get_path(file);
   324     if (path)
   325     {
   326 	dir->razor_file_default=razor_file_default_opendir(path,error);
   327 	if (!dir->razor_file_default)
   328 	{
   329 	    g_free(dir);
   330 	    dir=NULL;
   331 	}
   332 	g_free(path);
   333     }
   334     else
   335     {
   336 	dir->enumerator=g_file_enumerate_children(file,
   337 	  G_FILE_ATTRIBUTE_STANDARD_NAME,G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
   338 	  NULL,&err);
   339 	if (!dir->enumerator)
   340 	{
   341 	    g_free(dir);
   342 	    dir=NULL;
   343 	    plover_propagate_g_error(error,err);
   344 	}
   345     }
   346     g_object_unref(file);
   347     return dir;
   348 }
   349 
   350 char *file_readdir(void *dp,struct razor_error **error)
   351 {
   352     struct file_dir *dir=dp;
   353     char *name;
   354     GError *err=NULL;
   355     GFileInfo *info;
   356     g_message("file_readdir(%p)",dp);
   357     if (dir->razor_file_default)
   358 	name=razor_file_default_readdir(dir->razor_file_default,error);
   359     else
   360     {
   361 	info=g_file_enumerator_next_file(dir->enumerator,NULL,&err);
   362 	if (!info)
   363 	{
   364 	    name=NULL;
   365 	    plover_propagate_g_error(error,err);
   366 	}
   367 	else
   368 	{
   369 	    name=strdup(g_file_info_get_name(info));
   370 	    g_object_unref(info);
   371 	}
   372     }
   373     return name;
   374 }
   375 
   376 int file_closedir(void *dp,struct razor_error **error)
   377 {
   378     struct file_dir *dir=dp;
   379     int retval;
   380     GError *err=NULL;
   381     g_message("file_closedir(%p)",dp);
   382     if (dir->razor_file_default)
   383 	retval=razor_file_default_closedir(dir->razor_file_default,error);
   384     else
   385     {
   386 	retval=g_file_enumerator_close(dir->enumerator,NULL,&err)?0:-1;
   387 	if (retval)
   388 	    plover_propagate_g_error(error,err);
   389 	g_object_unref(dir->enumerator);
   390     }
   391     g_free(dir);
   392     return retval;
   393 }
   394 
   395 void plover__file_io_init(void)
   396 {
   397     static gsize init=0;
   398     struct razor_file_vtable file_vtable={0,};
   399     g_message("plover__file_io_init()");
   400     if (g_once_init_enter(&init))
   401     {
   402 	file_vtable.structure_size=sizeof(file_vtable);
   403 	file_vtable.mkdir=file_mkdir;
   404 	file_vtable.unlink=file_unlink;
   405 	file_vtable.open=file_open;
   406 	file_vtable.move=file_move;
   407 	file_vtable.get_contents=file_get_contents;
   408 	file_vtable.free_contents=file_free_contents;
   409 	file_vtable.is_directory=file_is_directory;
   410 	file_vtable.mkdtemp_near=file_mkdtemp_near;
   411 	file_vtable.opendir=file_opendir;
   412 	file_vtable.readdir=file_readdir;
   413 	file_vtable.closedir=file_closedir;
   414 	razor_file_set_vtable(&file_vtable);
   415 	g_once_init_leave(&init,1);
   416     }
   417 }