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