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