1 /*****************************************************************************
2 *
3 * Copyright (c) 2008-2010, CoreCodec, Inc.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of CoreCodec, Inc. nor the
14 * names of its contributors may be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY CoreCodec, Inc. ``AS IS'' AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL CoreCodec, Inc. BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 ****************************************************************************/
29
30 #include "file.h"
31
32 #ifdef CONFIG_FILEPOS_64
33 #define __USE_FILE_OFFSET64
34 #endif
35
36 #include <stdio.h>
37 #include <fcntl.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <unistd.h>
41 #include <dirent.h>
42 #include <errno.h>
43 #if defined(TARGET_OSX) || defined(__DragonFly__)
44 #include <sys/mount.h>
45 #elif defined(TARGET_QNX)
46 #include <sys/statvfs.h>
47 #else
48 #include <sys/vfs.h>
49 #endif
50
51 #if defined(O_ACCMODE)
52 #define _RW_ACCESS_FILE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
53 #define _RW_ACCESS_DIR (S_IRWXU|S_IRWXG|S_IRWXO)
54 #elif defined(S_IREAD) && defined(S_IWRITE)
55 #define _RW_ACCESS_FILE (S_IREAD|S_IWRITE)
56 #define _RW_ACCESS_DIR (S_IREAD|S_IWRITE|S_IEXEC)
57 #endif
58
59 typedef struct filestream
60 {
61 stream Stream;
62 int fd;
63 tchar_t URL[MAXPATH]; // TODO: turn into a dynamic data
64 filepos_t Length;
65 int Flags;
66
67 tchar_t DirPath[MAXPATH]; // TODO: turn into a dynamic data
68 DIR *FindDir;
69
70 } filestream;
71
Open(filestream * p,const tchar_t * URL,int Flags)72 static err_t Open(filestream* p, const tchar_t* URL, int Flags)
73 {
74 if (p->fd != -1)
75 close(p->fd);
76
77 p->Length = INVALID_FILEPOS_T;
78 p->fd = -1;
79
80 if (URL && URL[0])
81 {
82 struct stat file_stats;
83 int mode = 0;
84
85 if (Flags & SFLAG_WRONLY && !(Flags & SFLAG_RDONLY))
86 mode = O_WRONLY;
87 else if (Flags & SFLAG_RDONLY && !(Flags & SFLAG_WRONLY))
88 mode = O_RDONLY;
89 else
90 mode = O_RDWR;
91
92 if (Flags & SFLAG_CREATE)
93 mode |= O_CREAT|O_TRUNC;
94
95 //TODO: verify it works with Unicode files too
96 p->fd = open(URL, mode, _RW_ACCESS_FILE);
97 if (p->fd == -1)
98 {
99 if ((Flags & (SFLAG_REOPEN|SFLAG_SILENT))==0)
100 NodeReportError(p,NULL,ERR_ID,ERR_FILE_NOT_FOUND,URL);
101 return ERR_FILE_NOT_FOUND;
102 }
103
104 tcscpy_s(p->URL,TSIZEOF(p->URL),URL);
105
106 if (stat(URL, &file_stats) == 0)
107 p->Length = file_stats.st_size;
108
109 }
110 return ERR_NONE;
111 }
112
Read(filestream * p,void * Data,size_t Size,size_t * Readed)113 static err_t Read(filestream* p,void* Data,size_t Size,size_t* Readed)
114 {
115 err_t Err;
116 int n = read(p->fd, Data, (unsigned int)Size);
117 if (n<0)
118 {
119 n=0;
120 Err = ERR_READ;
121 }
122 else
123 Err = ((size_t)n != Size) ? ERR_END_OF_FILE:ERR_NONE;
124
125 if (Readed)
126 *Readed = n;
127 return Err;
128 }
129
ReadBlock(filestream * p,block * Block,size_t Ofs,size_t Size,size_t * Readed)130 static err_t ReadBlock(filestream* p,block* Block,size_t Ofs,size_t Size,size_t* Readed)
131 {
132 return Read(p,(void*)(Block->Ptr+Ofs),Size,Readed);
133 }
134
Write(filestream * p,const void * Data,size_t Size,size_t * Written)135 static err_t Write(filestream* p,const void* Data,size_t Size,size_t* Written)
136 {
137 err_t Err;
138 int n = write(p->fd, Data, (unsigned int)Size);
139
140 if (n<0)
141 {
142 n=0;
143 Err = ERR_WRITE;
144 }
145 else
146 Err = (n != Size) ? ERR_WRITE:ERR_NONE;
147
148 if (Written)
149 *Written = n;
150 return Err;
151 }
152
Seek(filestream * p,filepos_t Pos,int SeekMode)153 static filepos_t Seek(filestream* p,filepos_t Pos,int SeekMode)
154 {
155 off_t NewPos = lseek(p->fd, Pos, SeekMode);
156 if (NewPos<0)
157 return INVALID_FILEPOS_T;
158 return NewPos;
159 }
160
SetLength(filestream * p,dataid Id,const filepos_t * Data,size_t Size)161 static err_t SetLength(filestream* p,dataid Id,const filepos_t* Data,size_t Size)
162 {
163 if (Size != sizeof(filepos_t))
164 return ERR_INVALID_DATA;
165
166 if (ftruncate(p->fd, *Data)!=0)
167 return ERR_BUFFER_FULL;
168
169 return ERR_NONE;
170 }
171
OpenDir(filestream * p,const tchar_t * Path,int UNUSED_PARAM (Flags))172 static err_t OpenDir(filestream* p,const tchar_t* Path,int UNUSED_PARAM(Flags))
173 {
174 if (p->FindDir)
175 closedir(p->FindDir);
176
177 if (Path[0]==0)
178 Path = T("/");
179
180 p->FindDir = opendir(Path);
181 if (!p->FindDir)
182 {
183 if (errno == ENOTDIR)
184 return ERR_NOT_DIRECTORY;
185 else
186 return ERR_FILE_NOT_FOUND;
187 }
188
189 tcscpy_s(p->DirPath,TSIZEOF(p->DirPath),Path);
190 AddPathDelimiter(p->DirPath,TSIZEOF(p->DirPath));
191 return ERR_NONE;
192 }
193
194 extern datetime_t LinuxToDateTime(time_t);
195
EnumDir(filestream * p,const tchar_t * Exts,bool_t ExtFilter,streamdir * Item)196 static err_t EnumDir(filestream* p,const tchar_t* Exts,bool_t ExtFilter,streamdir* Item)
197 {
198 struct dirent *Dirent;
199
200 if (!p->FindDir)
201 return ERR_END_OF_FILE;
202
203 Item->FileName[0] = 0;
204 Item->Size = INVALID_FILEPOS_T;
205
206 while (!Item->FileName[0] && (Dirent = readdir(p->FindDir)) != NULL)
207 {
208 tchar_t FilePath[MAXPATH];
209 struct stat file_stats;
210
211 if (Dirent->d_name[0]=='.') // skip hidden files and current directory
212 continue;
213
214 tcscpy_s(FilePath, TSIZEOF(FilePath), p->DirPath);
215 tcscat_s(FilePath, TSIZEOF(FilePath), Dirent->d_name);
216 tcscpy_s(Item->FileName,TSIZEOF(Item->FileName), Dirent->d_name);
217
218 stat(FilePath, &file_stats);
219
220 Item->ModifiedDate = LinuxToDateTime(file_stats.st_mtime);
221 if (S_ISDIR(file_stats.st_mode))
222 {
223 Item->Type = FTYPE_DIR;
224 Item->Size = INVALID_FILEPOS_T;
225 }
226 else
227 {
228 Item->Size = file_stats.st_size;
229 Item->Type = CheckExts(Item->FileName,Exts);
230
231 if (!Item->Type && ExtFilter)
232 Item->FileName[0] = 0; // skip
233 }
234 }
235
236 if (!Item->FileName[0])
237 {
238 closedir(p->FindDir);
239 p->FindDir = NULL;
240 return ERR_END_OF_FILE;
241 }
242
243 return ERR_NONE;
244 }
245
Delete(filestream * p)246 static void Delete(filestream* p)
247 {
248 if (p->fd != -1)
249 {
250 close(p->fd);
251 p->fd = -1;
252 }
253 if (p->FindDir)
254 closedir(p->FindDir);
255 }
256
META_START(File_Class,FILE_CLASS)257 META_START(File_Class,FILE_CLASS)
258 META_CLASS(SIZE,sizeof(filestream))
259 META_CLASS(PRIORITY,PRI_MINIMUM)
260 META_CLASS(DELETE,Delete)
261 META_VMT(TYPE_FUNC,stream_vmt,Open,Open)
262 META_VMT(TYPE_FUNC,stream_vmt,Read,Read)
263 META_VMT(TYPE_FUNC,stream_vmt,ReadBlock,ReadBlock)
264 META_VMT(TYPE_FUNC,stream_vmt,Write,Write)
265 META_VMT(TYPE_FUNC,stream_vmt,Seek,Seek)
266 META_VMT(TYPE_FUNC,stream_vmt,OpenDir,OpenDir)
267 META_VMT(TYPE_FUNC,stream_vmt,EnumDir,EnumDir)
268 META_CONST(TYPE_INT,filestream,fd,-1)
269 META_DATA_RDONLY(TYPE_INT,STREAM_FLAGS,filestream,Flags)
270 META_DATA_RDONLY(TYPE_STRING,STREAM_URL,filestream,URL)
271 META_DATA_RDONLY(TYPE_STRING,STREAM_ENUM_BASE,filestream,DirPath)
272 META_PARAM(SET,STREAM_LENGTH,SetLength)
273 META_DATA(TYPE_FILEPOS,STREAM_LENGTH,filestream,Length)
274 META_PARAM(STRING,NODE_PROTOCOL,T("file"))
275 META_END(STREAM_CLASS)
276
277 bool_t FileExists(nodecontext *p,const tchar_t* Path)
278 {
279 struct stat file_stats;
280 return stat(Path, &file_stats) == 0;
281 }
282
FileErase(nodecontext * p,const tchar_t * Path,bool_t Force,bool_t Safe)283 bool_t FileErase(nodecontext *p,const tchar_t* Path, bool_t Force, bool_t Safe)
284 {
285 if (Force)
286 {
287 struct stat file_stats;
288 if (stat(Path, &file_stats) == 0)
289 {
290 if ((file_stats.st_mode & S_IWUSR)==0)
291 {
292 file_stats.st_mode |= S_IWUSR;
293 chmod(Path,file_stats.st_mode);
294 }
295 }
296 }
297 return unlink(Path) == 0;
298 }
299
FolderErase(nodecontext * p,const tchar_t * Path,bool_t Force,bool_t Safe)300 bool_t FolderErase(nodecontext *p,const tchar_t* Path, bool_t Force, bool_t Safe)
301 {
302 if (Force)
303 {
304 struct stat file_stats;
305 if (stat(Path, &file_stats) == 0)
306 {
307 if ((file_stats.st_mode & S_IWUSR)==0)
308 {
309 file_stats.st_mode |= S_IWUSR;
310 chmod(Path,file_stats.st_mode);
311 }
312 }
313 }
314 return rmdir(Path) == 0;
315 }
316
PathIsFolder(nodecontext * p,const tchar_t * Path)317 bool_t PathIsFolder(nodecontext *p,const tchar_t* Path)
318 {
319 struct stat file_stats;
320 if (stat(Path, &file_stats) == 0)
321 {
322 return (file_stats.st_mode & S_IFDIR) == S_IFDIR;
323 }
324 return 0;
325 }
326
FileDateTime(nodecontext * p,const tchar_t * Path)327 datetime_t FileDateTime(nodecontext *p,const tchar_t* Path)
328 {
329 datetime_t Date = INVALID_DATETIME_T;
330 struct stat file_stats;
331 if (stat(Path, &file_stats) == 0)
332 Date = LinuxToDateTime(file_stats.st_mtime);
333 return Date;
334 }
335
FileMove(nodecontext * p,const tchar_t * In,const tchar_t * Out)336 bool_t FileMove(nodecontext *p,const tchar_t* In,const tchar_t* Out)
337 {
338 return rename(In,Out) == 0;
339 }
340
FolderCreate(nodecontext * p,const tchar_t * Path)341 bool_t FolderCreate(nodecontext *p,const tchar_t* Path)
342 {
343 return mkdir(Path,_RW_ACCESS_DIR) == 0;
344 }
345
FindFiles(nodecontext * p,const tchar_t * Path,const tchar_t * Mask,void (* Process)(const tchar_t *,void *),void * Param)346 void FindFiles(nodecontext *p,const tchar_t* Path, const tchar_t* Mask,void(*Process)(const tchar_t*,void*),void* Param)
347 {
348 DIR* Directory;
349 struct dirent* DirectoryInfo;
350 tchar_t TPathToFile[MAXPATH];
351
352 Directory = opendir(Path);
353 if (Directory)
354 {
355 while ( (DirectoryInfo = readdir(Directory)) != NULL )
356 {
357 char* FileExtension = 0;
358 FileExtension = strrchr(DirectoryInfo->d_name, '.');
359 if(FileExtension)
360 {
361 if (strcmp(Mask, FileExtension ) == 0 )
362 {
363 tcscpy_s(TPathToFile, TSIZEOF(TPathToFile), Path);
364 tcscat_s(TPathToFile, TSIZEOF(TPathToFile), DirectoryInfo->d_name);
365 Process(TPathToFile, Param);
366 }
367 }
368 }
369
370 closedir(Directory);
371 }
372
373 }
374
FileTemp(anynode * Any)375 stream *FileTemp(anynode *Any)
376 {
377 #ifndef TODO
378 assert(NULL); // not supported yet
379 #endif
380 return NULL;
381 }
382
FileTempName(anynode * Any,tchar_t * Out,size_t OutLen)383 bool_t FileTempName(anynode *Any,tchar_t *Out, size_t OutLen)
384 {
385 #ifndef TODO
386 assert(NULL); // not supported yet
387 #endif
388 return 0;
389 }
390
GetPathFreeSpace(nodecontext * UNUSED_PARAM (p),const tchar_t * Path)391 int64_t GetPathFreeSpace(nodecontext* UNUSED_PARAM(p), const tchar_t* Path)
392 {
393 #ifndef TODO
394 // need to an include (see at includes)
395 #if defined(TARGET_QNX)
396 struct statvfs st;
397 if (statvfs(Path, &st) < 0)
398 #else
399 struct statfs st;
400 if (statfs(Path, &st) < 0)
401 #endif
402 return -1;
403 return (int64_t)st.f_bsize * (int64_t)st.f_bavail;
404 #else
405 return -1;
406 #endif
407 }
408