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(TARGET_FREEBSD)
44 #include <sys/mount.h>
45 #else
46 #include <sys/vfs.h>
47 #endif
48
49 #if defined(O_ACCMODE)
50 #define _RW_ACCESS_FILE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
51 #define _RW_ACCESS_DIR (S_IRWXU|S_IRWXG|S_IRWXO)
52 #elif defined(S_IREAD) && defined(S_IWRITE)
53 #define _RW_ACCESS_FILE (S_IREAD|S_IWRITE)
54 #define _RW_ACCESS_DIR (S_IREAD|S_IWRITE|S_IEXEC)
55 #endif
56
57 typedef struct filestream
58 {
59 stream Stream;
60 int fd;
61 tchar_t URL[MAXPATH]; // TODO: turn into a dynamic data
62 filepos_t Length;
63 int Flags;
64
65 tchar_t DirPath[MAXPATH]; // TODO: turn into a dynamic data
66 DIR *FindDir;
67
68 } filestream;
69
Open(filestream * p,const tchar_t * URL,int Flags)70 static err_t Open(filestream* p, const tchar_t* URL, int Flags)
71 {
72 if (p->fd != -1)
73 close(p->fd);
74
75 p->Length = INVALID_FILEPOS_T;
76 p->fd = -1;
77
78 if (URL && URL[0])
79 {
80 struct stat file_stats;
81 int mode = 0;
82
83 if (Flags & SFLAG_WRONLY && !(Flags & SFLAG_RDONLY))
84 mode = O_WRONLY;
85 else if (Flags & SFLAG_RDONLY && !(Flags & SFLAG_WRONLY))
86 mode = O_RDONLY;
87 else
88 mode = O_RDWR;
89
90 if (Flags & SFLAG_CREATE)
91 mode |= O_CREAT|O_TRUNC;
92
93 //TODO: verify it works with Unicode files too
94 p->fd = open(URL, mode, _RW_ACCESS_FILE);
95 if (p->fd == -1)
96 {
97 if ((Flags & (SFLAG_REOPEN|SFLAG_SILENT))==0)
98 NodeReportError(p,NULL,ERR_ID,ERR_FILE_NOT_FOUND,URL);
99 return ERR_FILE_NOT_FOUND;
100 }
101
102 tcscpy_s(p->URL,TSIZEOF(p->URL),URL);
103
104 if (stat(URL, &file_stats) == 0)
105 p->Length = file_stats.st_size;
106
107 }
108 return ERR_NONE;
109 }
110
Read(filestream * p,void * Data,size_t Size,size_t * Readed)111 static err_t Read(filestream* p,void* Data,size_t Size,size_t* Readed)
112 {
113 err_t Err;
114 int n = read(p->fd, Data, (unsigned int)Size);
115 if (n<0)
116 {
117 n=0;
118 Err = ERR_READ;
119 }
120 else
121 Err = ((size_t)n != Size) ? ERR_END_OF_FILE:ERR_NONE;
122
123 if (Readed)
124 *Readed = n;
125 return Err;
126 }
127
ReadBlock(filestream * p,block * Block,size_t Ofs,size_t Size,size_t * Readed)128 static err_t ReadBlock(filestream* p,block* Block,size_t Ofs,size_t Size,size_t* Readed)
129 {
130 return Read(p,(void*)(Block->Ptr+Ofs),Size,Readed);
131 }
132
Write(filestream * p,const void * Data,size_t Size,size_t * Written)133 static err_t Write(filestream* p,const void* Data,size_t Size,size_t* Written)
134 {
135 err_t Err;
136 int n = write(p->fd, Data, (unsigned int)Size);
137
138 if (n<0)
139 {
140 n=0;
141 Err = ERR_WRITE;
142 }
143 else
144 Err = (n != Size) ? ERR_WRITE:ERR_NONE;
145
146 if (Written)
147 *Written = n;
148 return Err;
149 }
150
Seek(filestream * p,filepos_t Pos,int SeekMode)151 static filepos_t Seek(filestream* p,filepos_t Pos,int SeekMode)
152 {
153 off_t NewPos = lseek(p->fd, Pos, SeekMode);
154 if (NewPos<0)
155 return INVALID_FILEPOS_T;
156 return NewPos;
157 }
158
SetLength(filestream * p,dataid Id,const filepos_t * Data,size_t Size)159 static err_t SetLength(filestream* p,dataid Id,const filepos_t* Data,size_t Size)
160 {
161 if (Size != sizeof(filepos_t))
162 return ERR_INVALID_DATA;
163
164 if (ftruncate(p->fd, *Data)!=0)
165 return ERR_BUFFER_FULL;
166
167 return ERR_NONE;
168 }
169
OpenDir(filestream * p,const tchar_t * Path,int UNUSED_PARAM (Flags))170 static err_t OpenDir(filestream* p,const tchar_t* Path,int UNUSED_PARAM(Flags))
171 {
172 if (p->FindDir)
173 closedir(p->FindDir);
174
175 if (Path[0]==0)
176 Path = T("/");
177
178 p->FindDir = opendir(Path);
179 if (!p->FindDir)
180 {
181 if (errno == ENOTDIR)
182 return ERR_NOT_DIRECTORY;
183 else
184 return ERR_FILE_NOT_FOUND;
185 }
186
187 tcscpy_s(p->DirPath,TSIZEOF(p->DirPath),Path);
188 AddPathDelimiter(p->DirPath,TSIZEOF(p->DirPath));
189 return ERR_NONE;
190 }
191
192 extern datetime_t LinuxToDateTime(time_t);
193
EnumDir(filestream * p,const tchar_t * Exts,bool_t ExtFilter,streamdir * Item)194 static err_t EnumDir(filestream* p,const tchar_t* Exts,bool_t ExtFilter,streamdir* Item)
195 {
196 struct dirent *Dirent;
197
198 if (!p->FindDir)
199 return ERR_END_OF_FILE;
200
201 Item->FileName[0] = 0;
202 Item->Size = INVALID_FILEPOS_T;
203
204 while (!Item->FileName[0] && (Dirent = readdir(p->FindDir)) != NULL)
205 {
206 tchar_t FilePath[MAXPATH];
207 struct stat file_stats;
208
209 if (Dirent->d_name[0]=='.') // skip hidden files and current directory
210 continue;
211
212 tcscpy_s(FilePath, TSIZEOF(FilePath), p->DirPath);
213 tcscat_s(FilePath, TSIZEOF(FilePath), Dirent->d_name);
214 tcscpy_s(Item->FileName,TSIZEOF(Item->FileName), Dirent->d_name);
215
216 stat(FilePath, &file_stats);
217
218 Item->ModifiedDate = LinuxToDateTime(file_stats.st_mtime);
219 if (S_ISDIR(file_stats.st_mode))
220 {
221 Item->Type = FTYPE_DIR;
222 Item->Size = INVALID_FILEPOS_T;
223 }
224 else
225 {
226 Item->Size = file_stats.st_size;
227 Item->Type = CheckExts(Item->FileName,Exts);
228
229 if (!Item->Type && ExtFilter)
230 Item->FileName[0] = 0; // skip
231 }
232 }
233
234 if (!Item->FileName[0])
235 {
236 closedir(p->FindDir);
237 p->FindDir = NULL;
238 return ERR_END_OF_FILE;
239 }
240
241 return ERR_NONE;
242 }
243
Delete(filestream * p)244 static void Delete(filestream* p)
245 {
246 if (p->fd != -1)
247 {
248 close(p->fd);
249 p->fd = -1;
250 }
251 if (p->FindDir)
252 closedir(p->FindDir);
253 }
254
META_START(File_Class,FILE_CLASS)255 META_START(File_Class,FILE_CLASS)
256 META_CLASS(SIZE,sizeof(filestream))
257 META_CLASS(PRIORITY,PRI_MINIMUM)
258 META_CLASS(DELETE,Delete)
259 META_VMT(TYPE_FUNC,stream_vmt,Open,Open)
260 META_VMT(TYPE_FUNC,stream_vmt,Read,Read)
261 META_VMT(TYPE_FUNC,stream_vmt,ReadBlock,ReadBlock)
262 META_VMT(TYPE_FUNC,stream_vmt,Write,Write)
263 META_VMT(TYPE_FUNC,stream_vmt,Seek,Seek)
264 META_VMT(TYPE_FUNC,stream_vmt,OpenDir,OpenDir)
265 META_VMT(TYPE_FUNC,stream_vmt,EnumDir,EnumDir)
266 META_CONST(TYPE_INT,filestream,fd,-1)
267 META_DATA_RDONLY(TYPE_INT,STREAM_FLAGS,filestream,Flags)
268 META_DATA_RDONLY(TYPE_STRING,STREAM_URL,filestream,URL)
269 META_DATA_RDONLY(TYPE_STRING,STREAM_ENUM_BASE,filestream,DirPath)
270 META_PARAM(SET,STREAM_LENGTH,SetLength)
271 META_DATA(TYPE_FILEPOS,STREAM_LENGTH,filestream,Length)
272 META_PARAM(STRING,NODE_PROTOCOL,T("file"))
273 META_END(STREAM_CLASS)
274
275 bool_t FileExists(nodecontext *p,const tchar_t* Path)
276 {
277 struct stat file_stats;
278 return stat(Path, &file_stats) == 0;
279 }
280
FileErase(nodecontext * p,const tchar_t * Path,bool_t Force,bool_t Safe)281 bool_t FileErase(nodecontext *p,const tchar_t* Path, bool_t Force, bool_t Safe)
282 {
283 if (Force)
284 {
285 struct stat file_stats;
286 if (stat(Path, &file_stats) == 0)
287 {
288 if ((file_stats.st_mode & S_IWUSR)==0)
289 {
290 file_stats.st_mode |= S_IWUSR;
291 chmod(Path,file_stats.st_mode);
292 }
293 }
294 }
295 return unlink(Path) == 0;
296 }
297
FolderErase(nodecontext * p,const tchar_t * Path,bool_t Force,bool_t Safe)298 bool_t FolderErase(nodecontext *p,const tchar_t* Path, bool_t Force, bool_t Safe)
299 {
300 if (Force)
301 {
302 struct stat file_stats;
303 if (stat(Path, &file_stats) == 0)
304 {
305 if ((file_stats.st_mode & S_IWUSR)==0)
306 {
307 file_stats.st_mode |= S_IWUSR;
308 chmod(Path,file_stats.st_mode);
309 }
310 }
311 }
312 return rmdir(Path) == 0;
313 }
314
PathIsFolder(nodecontext * p,const tchar_t * Path)315 bool_t PathIsFolder(nodecontext *p,const tchar_t* Path)
316 {
317 struct stat file_stats;
318 if (stat(Path, &file_stats) == 0)
319 {
320 return (file_stats.st_mode & S_IFDIR) == S_IFDIR;
321 }
322 return 0;
323 }
324
FileDateTime(nodecontext * p,const tchar_t * Path)325 datetime_t FileDateTime(nodecontext *p,const tchar_t* Path)
326 {
327 datetime_t Date = INVALID_DATETIME_T;
328 struct stat file_stats;
329 if (stat(Path, &file_stats) == 0)
330 Date = LinuxToDateTime(file_stats.st_mtime);
331 return Date;
332 }
333
FileMove(nodecontext * p,const tchar_t * In,const tchar_t * Out)334 bool_t FileMove(nodecontext *p,const tchar_t* In,const tchar_t* Out)
335 {
336 return rename(In,Out) == 0;
337 }
338
FolderCreate(nodecontext * p,const tchar_t * Path)339 bool_t FolderCreate(nodecontext *p,const tchar_t* Path)
340 {
341 return mkdir(Path,_RW_ACCESS_DIR) == 0;
342 }
343
FindFiles(nodecontext * p,const tchar_t * Path,const tchar_t * Mask,void (* Process)(const tchar_t *,void *),void * Param)344 void FindFiles(nodecontext *p,const tchar_t* Path, const tchar_t* Mask,void(*Process)(const tchar_t*,void*),void* Param)
345 {
346 DIR* Directory;
347 struct dirent* DirectoryInfo;
348 tchar_t TPathToFile[MAXPATH];
349
350 Directory = opendir(Path);
351 if (Directory)
352 {
353 while ( (DirectoryInfo = readdir(Directory)) != NULL )
354 {
355 char* FileExtension = 0;
356 FileExtension = strrchr(DirectoryInfo->d_name, '.');
357 if(FileExtension)
358 {
359 if (strcmp(Mask, FileExtension ) == 0 )
360 {
361 tcscpy_s(TPathToFile, TSIZEOF(TPathToFile), Path);
362 tcscat_s(TPathToFile, TSIZEOF(TPathToFile), DirectoryInfo->d_name);
363 Process(TPathToFile, Param);
364 }
365 }
366 }
367
368 closedir(Directory);
369 }
370
371 }
372
FileTemp(anynode * Any)373 stream *FileTemp(anynode *Any)
374 {
375 #ifndef TODO
376 assert(NULL); // not supported yet
377 #endif
378 return NULL;
379 }
380
FileTempName(anynode * Any,tchar_t * Out,size_t OutLen)381 bool_t FileTempName(anynode *Any,tchar_t *Out, size_t OutLen)
382 {
383 #ifndef TODO
384 assert(NULL); // not supported yet
385 #endif
386 return 0;
387 }
388
GetPathFreeSpace(nodecontext * UNUSED_PARAM (p),const tchar_t * Path)389 int64_t GetPathFreeSpace(nodecontext* UNUSED_PARAM(p), const tchar_t* Path)
390 {
391 #ifndef TODO
392 // need to an include (see at includes)
393 struct statfs st;
394 if (statfs(Path, &st) < 0)
395 return -1;
396 return (int64_t)st.f_bsize * (int64_t)st.f_bavail;
397 #else
398 return -1;
399 #endif
400 }
401