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