1 /*===========================================================================
2  *
3  *                            PUBLIC DOMAIN NOTICE
4  *               National Center for Biotechnology Information
5  *
6  *  This software/database is a "United States Government Work" under the
7  *  terms of the United States Copyright Act.  It was written as part of
8  *  the author's official duties as a United States Government employee and
9  *  thus cannot be copyrighted.  This software/database is freely available
10  *  to the public for use. The National Library of Medicine and the U.S.
11  *  Government have not placed any restriction on its use or reproduction.
12  *
13  *  Although all reasonable efforts have been taken to ensure the accuracy
14  *  and reliability of the software and data, the NLM and the U.S.
15  *  Government do not and cannot warrant the performance or results that
16  *  may be obtained by using this software or data. The NLM and the U.S.
17  *  Government disclaim all warranties, express or implied, including
18  *  warranties of performance, merchantability or fitness for any particular
19  *  purpose.
20  *
21  *  Please cite the author in any work or product based on this material.
22  *
23  * ===========================================================================
24  *
25  */
26 #include <klib/namelist.h>
27 #include <klib/printf.h>
28 #include <kxml/xml.h>
29 #include <kfs/directory.h>
30 #include <kfs/file.h>
31 #include <krypto/encfile.h>
32 #include <krypto/key.h>
33 
34 typedef struct FileNode FileNode;
35 #define FSNODE_IMPL FileNode
36 
37 #include "log.h"
38 #include "xml.h"
39 #include "file.h"
40 #include "kfile-accessor.h"
41 
42 #include <string.h>
43 #include <stdlib.h>
44 #include <time.h>
45 
46 struct FileNode {
47     FSNode node;
48     char* path;
49     KTime_t mtime;
50     KKey key;
51 };
52 
53 static
FileNode_Attr(const FileNode * cself,const char * subpath,uint32_t * type,KTime_t * ts,uint64_t * file_sz,uint32_t * access,uint64_t * block_sz)54 rc_t FileNode_Attr(const FileNode* cself, const char* subpath, uint32_t* type, KTime_t* ts, uint64_t* file_sz, uint32_t* access, uint64_t* block_sz)
55 {
56     rc_t rc = 0;
57     KDirectory* dir = NULL;
58 
59     if( subpath != NULL ) {
60         rc = RC(rcExe, rcFile, rcEvaluating, rcDirEntry, rcNotFound);
61     } else if( (rc = KDirectoryNativeDir(&dir)) == 0 ) {
62         *type = KDirectoryPathType(dir, "%s", cself->path);
63         DEBUG_LINE(8, "file type %x", *type);
64         if( cself->mtime != 0 ) {
65             *ts = cself->mtime;
66         } else if( (rc = KDirectoryDate(dir, ts, "%s", cself->path)) == 0 ) {
67             DEBUG_LINE(8, "file mtime %u", *ts);
68         }
69         if( rc == 0 && (rc = KDirectoryAccess(dir, access, "%s", cself->path)) == 0 ) {
70             DEBUG_LINE(8, "file access %x", *access);
71             if( *type & kptAlias ) {
72                 char r[10240];
73                 if( (rc = KDirectoryResolveAlias(dir, true, r, sizeof(r), "%s", cself->path)) == 0 ) {
74                     *file_sz = strlen(r);
75                 }
76             } else if( *type == kptFile ) {
77                 rc = KDirectoryFileSize(dir, file_sz, "%s", cself->path);
78             }
79         }
80         ReleaseComplain(KDirectoryRelease, dir);
81     }
82     return rc;
83 }
84 
85 static
FileNode_Link(const FileNode * cself,const char * subpath,char * buf,size_t buf_sz)86 rc_t FileNode_Link(const FileNode* cself, const char* subpath, char* buf, size_t buf_sz)
87 {
88     rc_t rc = 0;
89     KDirectory* dir = NULL;
90 
91     if( (rc = KDirectoryNativeDir(&dir)) == 0 ) {
92         rc = KDirectoryResolveAlias(dir, true, buf, buf_sz, "%s", cself->path);
93         ReleaseComplain(KDirectoryRelease, dir);
94     }
95     return rc;
96 }
97 
98 static
FileNode_Open(const FileNode * cself,const char * subpath,const SAccessor ** accessor)99 rc_t FileNode_Open(const FileNode* cself, const char* subpath, const SAccessor** accessor)
100 {
101     rc_t rc = 0;
102 
103     if( subpath != NULL ) {
104         rc = RC(rcExe, rcFile, rcOpening, rcDirEntry, rcNotFound);
105     } else {
106         KDirectory* dir = NULL;
107         if( (rc = KDirectoryNativeDir(&dir)) == 0 ) {
108             const KFile* kf = NULL;
109             const KFile* enc_kf = NULL;
110             const KFile* immediate = NULL;
111             if( (rc = KDirectoryOpenFileRead(dir, &kf, "%s", cself->path)) == 0 ) {
112                 immediate = kf;
113                 if( cself->key.type != kkeyNone ) {
114                     /* TODO: what is the correct way to release KFile objects */
115                     rc = KEncFileMakeRead (&enc_kf, kf, &cself->key);
116                     immediate = enc_kf;
117                 }
118                 if( rc == 0 ) {
119                     if( (rc = KFileAccessor_Make(accessor, cself->node.name, immediate)) != 0 ) {
120                         ReleaseComplain(KFileRelease, immediate);
121                     }
122                 }
123 
124             }
125             ReleaseComplain(KDirectoryRelease, dir);
126         }
127     }
128     if( rc != 0 ) {
129         SAccessor_Release(*accessor);
130         *accessor = NULL;
131     }
132     return rc;
133 }
134 
135 static
FileNode_Release(FileNode * self)136 rc_t FileNode_Release(FileNode* self)
137 {
138     if( self != NULL ) {
139         FREE(self->path);
140     }
141     return 0;
142 }
143 
144 static FSNode_vtbl FileNode_vtbl = {
145     sizeof(FileNode),
146     NULL,
147     NULL,
148     FileNode_Attr,
149     NULL,
150     FileNode_Link,
151     FileNode_Open,
152     FileNode_Release
153 };
154 
FileNode_Make(const KXMLNode * xml_node,FSNode ** cself,char * errmsg,const char * rel_path,EXMLValidate validate)155 rc_t FileNode_Make(const KXMLNode* xml_node, FSNode** cself, char* errmsg, const char* rel_path, EXMLValidate validate)
156 {
157     rc_t rc = 0;
158 
159     if( xml_node == NULL || cself == NULL || errmsg == NULL || rel_path == NULL ) {
160         rc = RC(rcExe, rcNode, rcConstructing, rcParam, rcNull);
161     } else {
162         char* path = NULL, *name = NULL, name_buf[4096], password[4096];
163         KTime_t ktm = 0;
164         FileNode* ff = NULL;
165         size_t password_sz = 0;
166 
167         if( (rc = KXMLNodeReadAttrCStr(xml_node, "path", &path, NULL)) == 0 ) {
168             if( path[0] == '\0' ) {
169                 rc = RC(rcExe, rcDoc, rcValidating, rcDirEntry, rcInvalid);
170             } else {
171                 KDirectory* dir = NULL;
172                 if( (rc = KDirectoryNativeDir(&dir)) == 0 ) {
173                     if( path[0] != '/' ) {
174                         char resolved[4096];
175                         if( (rc = KDirectoryResolvePath(dir, true, resolved, sizeof(resolved),
176                                                                   "%s%s", rel_path, path)) == 0 ) {
177                             DEBUG_LINE(8, "%s%s resolved to %s", rel_path, path, resolved);
178                             FREE(path);
179                             rc = StrDup(resolved, &path);
180                         }
181                     }
182                     if( rc == 0 && validate > eXML_NoCheck ) {
183                         uint32_t t = KDirectoryPathType(dir, "%s", path);
184                         if( (t != kptFile && t != (kptFile | kptAlias)) &&
185                             (t != kptCharDev && t != (kptCharDev | kptAlias)) &&
186                             (t != kptBlockDev && t != (kptBlockDev | kptAlias)) &&
187                             (t != kptFIFO && t != (kptFIFO | kptAlias))  ) {
188                             if( validate > eXML_NoFail ) {
189                                 rc = RC(rcExe, rcDoc, rcValidating, rcDirEntry, t == kptNotFound ? rcNotFound : rcInvalid);
190                             } else {
191                                 PLOGMSG(klogErr, (klogErr, "File path '$(p)' not found", "p=%s", path));
192                             }
193                         }
194                     }
195                     ReleaseComplain(KDirectoryRelease, dir);
196                 }
197             }
198         }
199         if( rc != 0 ) {
200             strcpy(errmsg, "File/@path: '");
201             strcat(errmsg, path ? path : "(null)");
202             strcat(errmsg, "'");
203         }
204         if( rc == 0 ) {
205             rc = KXMLNodeReadAttrCString(xml_node, "name", name_buf, sizeof(name_buf), &password_sz);
206             if( rc == 0 && name_buf[0] != '\0' ) {
207                 name = name_buf;
208             } else if( GetRCObject(rc) == (enum RCObject)rcAttr && GetRCState(rc) == rcNotFound ) {
209                 rc = 0;
210             }
211             if( rc != 0 ) {
212                 strcpy(errmsg, "File/@name");
213             } else if( name == NULL ) {
214                 name = strrchr(path, '/');
215                 name = name ? name + 1 : path;
216             }
217         }
218         if( rc == 0 && (rc = XML_ParseTimestamp(xml_node, "timestamp", &ktm, true)) != 0 ) {
219             strcpy(errmsg, "File/@timestamp");
220         }
221         if( rc == 0 ) {
222             rc = KXMLNodeReadAttrCString(xml_node, "password", password, sizeof(password), &password_sz);
223             if( rc == 0 || (GetRCObject(rc) == (enum RCObject)rcAttr && GetRCState(rc) == rcNotFound) ) {
224                 rc = 0;
225                 password_sz = 0;
226             } else {
227                 strcpy(errmsg, "File/@password");
228             }
229         }
230         if( rc == 0 ) {
231             struct KNamelist const* attr = NULL;
232             if( (rc = KXMLNodeListAttr(xml_node, &attr)) == 0 ) {
233                 uint32_t i = 0, count = 0;
234                 if( (rc = KNamelistCount(attr, &count)) == 0 && count > 0 ) {
235                     while( rc == 0 && i < count ) {
236                         const char *attr_nm = NULL;
237                         if( (rc = KNamelistGet(attr, i++, &attr_nm)) != 0 ) {
238                             break;
239                         }
240                         if( strcmp("path", attr_nm) == 0 || strcmp("name", attr_nm) == 0 ||
241                             strcmp("timestamp", attr_nm) == 0 || strcmp("password", attr_nm) == 0 ) {
242                             continue;
243                         }
244                         rc = RC(rcExe, rcDoc, rcValidating, rcDirEntry, rcInvalid);
245                         strcpy(errmsg, "unknown attribute File/@");
246                         strcat(errmsg, attr_nm);
247                     }
248                 }
249                 ReleaseComplain(KNamelistRelease, attr);
250             }
251         }
252         if( rc == 0 ) {
253             if( (rc = FSNode_Make((FSNode**)&ff, name, &FileNode_vtbl)) == 0 ) {
254                 ff->path = path;
255                 ff->mtime = ktm;
256                 if( password_sz > 0 ) {
257                     rc = KKeyInitRead(&ff->key, kkeyAES128, password, password_sz);
258                 } else {
259                     memset(&ff->key, 0, sizeof ff->key);
260                     ff->key.type = kkeyNone;
261                 }
262             } else {
263                 strcpy(errmsg, "File '");
264                 strcat(errmsg, name);
265                 strcat(errmsg, "'");
266             }
267         }
268         if( rc == 0 ) {
269             *cself = &ff->node;
270         } else {
271             FREE(path);
272         }
273     }
274     return rc;
275 }
276