1  /*
2   * UAE - The Un*x Amiga Emulator
3   *
4   * Library of functions to make emulated filesystem as independent as
5   * possible of the host filesystem's capabilities.
6   * This is the Unix version.
7   *
8   * Copyright 1999 Bernd Schmidt
9   */
10 
11 #include "sysconfig.h"
12 #include "sysdeps.h"
13 
14 #include "fsdb.h"
15 #include "misc.h"
16 
17 /* these are deadly (but I think allowed on the Amiga): */
18 #define NUM_EVILCHARS 7
19 static TCHAR evilchars[NUM_EVILCHARS] = { '\\', '*', '?', '\"', '<', '>', '|' };
20 
21 #define UAEFSDB_BEGINS "__uae___"
22 #define UAEFSDB_BEGINSX "__uae___*"
23 #define UAEFSDB_LEN 604
24 #define UAEFSDB2_LEN 1632
25 
26 /* The on-disk format is as follows:
27 * Offset 0, 1 byte, valid
28 * Offset 1, 4 bytes, mode
29 * Offset 5, 257 bytes, aname
30 * Offset 262, 257 bytes, nname
31 * Offset 519, 81 bytes, comment
32 * Offset 600, 4 bytes, Windows-side mode
33 *
34 * 1.6.0+ Unicode data
35 *
36 * Offset  604, 257 * 2 bytes, aname
37 * Offset 1118, 257 * 2 bytes, nname
38 *        1632
39 */
40 
41 #define TRACING_ENABLED 0
42 #if TRACING_ENABLED
43 #define TRACE(x)	do { write_log x; } while(0)
44 #else
45 #define TRACE(x)
46 #endif
47 
dos_errno(void)48 int dos_errno (void)
49 {
50     int e = errno;
51 
52     switch (e) {
53 	case ENOMEM:	return ERROR_NO_FREE_STORE;
54 	case EEXIST:	return ERROR_OBJECT_EXISTS;
55 	case EACCES:	return ERROR_WRITE_PROTECTED;
56 	case ENOENT:	return ERROR_OBJECT_NOT_AROUND;
57 	case ENOTDIR:	return ERROR_OBJECT_WRONG_TYPE;
58 	case ENOSPC:	return ERROR_DISK_IS_FULL;
59 	case EBUSY:       	return ERROR_OBJECT_IN_USE;
60 	case EISDIR:	return ERROR_OBJECT_WRONG_TYPE;
61 #if defined(ETXTBSY)
62 	case ETXTBSY:	return ERROR_OBJECT_IN_USE;
63 #endif
64 #if defined(EROFS)
65 	case EROFS:       	return ERROR_DISK_WRITE_PROTECTED;
66 #endif
67 #if defined(ENOTEMPTY)
68 #if ENOTEMPTY != EEXIST
69 	case ENOTEMPTY:	return ERROR_DIRECTORY_NOT_EMPTY;
70 #endif
71 #endif
72 	default:
73 	TRACE (("FSDB: Unimplemented error: %s\n", strerror (e)));
74 	return ERROR_NOT_IMPLEMENTED;
75     }
76 }
77 
fsdb_name_invalid_2(const TCHAR * n,int dir)78 static int fsdb_name_invalid_2 (const TCHAR *n, int dir)
79 {
80         int i;
81         static char s1[MAX_DPATH];
82         static TCHAR s2[MAX_DPATH];
83         TCHAR a = n[0];
84         TCHAR b = (a == '\0' ? a : n[1]);
85         TCHAR c = (b == '\0' ? b : n[2]);
86         int l = _tcslen (n);
87 
88         /* the reserved fsdb filename */
89         if (_tcscmp (n, FSDB_FILE) == 0)
90                 return -1;
91 
92         if (dir) {
93                 if (n[0] == '.' && l == 1)
94                         return -1;
95                 if (n[0] == '.' && n[1] == '.' && l == 2)
96                         return -1;
97         }
98 
99         if (a >= 'a' && a <= 'z')
100                 a -= 32;
101         if (b >= 'a' && b <= 'z')
102                 b -= 32;
103         if (c >= 'a' && c <= 'z')
104                 c -= 32;
105 
106         /* these characters are *never* allowed */
107         for (i = 0; i < NUM_EVILCHARS; i++) {
108                 if (_tcschr (n, evilchars[i]) != 0)
109                         return 1;
110         }
111 
112         s1[0] = 0;
113         s2[0] = 0;
114         //FIXME: ua_fs_copy (s1, MAX_DPATH, n, -1);
115         strcpy (s1, n);
116         au_fs_copy (s2, MAX_DPATH, s1);
117         if (_tcscmp (s2, n) != 0)
118                 return 1;
119 
120         return 0; /* the filename passed all checks, now it should be ok */
121 }
122 
fsdb_name_invalid(const TCHAR * n)123 int fsdb_name_invalid (const TCHAR *n)
124 {
125         int v = fsdb_name_invalid_2 (n, 0);
126         if (v <= 0)
127                 return v;
128         write_log (_T("FILESYS: '%s' illegal filename\n"), n);
129         return v;
130 }
131 
fsdb_name_invalid_dir(const TCHAR * n)132 int fsdb_name_invalid_dir (const TCHAR *n)
133 {
134         int v = fsdb_name_invalid_2 (n, 1);
135         if (v <= 0)
136                 return v;
137         write_log (_T("FILESYS: '%s' illegal filename\n"), n);
138         return v;
139 }
140 
fsdb_exists(const char * nname)141 int fsdb_exists (const char *nname)
142 {
143     struct stat statbuf;
144     return (stat (nname, &statbuf) != -1);
145 }
146 
147 /* For an a_inode we have newly created based on a filename we found on the
148  * native fs, fill in information about this file/directory.  */
fsdb_fill_file_attrs(a_inode * base,a_inode * aino)149 int fsdb_fill_file_attrs (a_inode *base, a_inode *aino)
150 {
151     struct stat statbuf;
152     /* This really shouldn't happen...  */
153     if (stat (aino->nname, &statbuf) == -1)
154 	return 0;
155     aino->dir = S_ISDIR (statbuf.st_mode) ? 1 : 0;
156     aino->amigaos_mode = ((S_IXUSR & statbuf.st_mode ? 0 : A_FIBF_EXECUTE)
157 			  | (S_IWUSR & statbuf.st_mode ? 0 : A_FIBF_WRITE)
158 			  | (S_IRUSR & statbuf.st_mode ? 0 : A_FIBF_READ));
159 #ifdef ANDROID
160     // Always give execute & read permission
161     aino->amigaos_mode &= ~A_FIBF_EXECUTE;
162     aino->amigaos_mode &= ~A_FIBF_READ;
163 #endif
164     return 1;
165 }
166 
fsdb_set_file_attrs(a_inode * aino)167 int fsdb_set_file_attrs (a_inode *aino)
168 {
169     struct stat statbuf;
170     int tmpmask = aino->amigaos_mode;
171     int mode;
172 
173     if (stat (aino->nname, &statbuf) == -1)
174 	return ERROR_OBJECT_NOT_AROUND;
175 
176     mode = statbuf.st_mode;
177     /* Unix dirs behave differently than AmigaOS ones.  */
178     if (! aino->dir) {
179 	if (tmpmask & A_FIBF_READ)
180 	    mode &= ~S_IRUSR;
181 	else
182 	    mode |= S_IRUSR;
183 
184 	if (tmpmask & A_FIBF_WRITE)
185 	    mode &= ~S_IWUSR;
186 	else
187 	    mode |= S_IWUSR;
188 
189 	if (tmpmask & A_FIBF_EXECUTE)
190 	    mode &= ~S_IXUSR;
191 	else
192 	    mode |= S_IXUSR;
193 
194 	chmod (aino->nname, mode);
195     }
196 
197     aino->dirty = 1;
198     return 0;
199 }
200 
201 /* return supported combination */
fsdb_mode_supported(const a_inode * aino)202 int fsdb_mode_supported (const a_inode *aino)
203 {
204         int mask = aino->amigaos_mode;
205         if (0 && aino->dir)
206                 return 0;
207         if (fsdb_mode_representable_p (aino, mask))
208                 return mask;
209         mask &= ~(A_FIBF_SCRIPT | A_FIBF_READ | A_FIBF_EXECUTE);
210         if (fsdb_mode_representable_p (aino, mask))
211                 return mask;
212         mask &= ~A_FIBF_WRITE;
213         if (fsdb_mode_representable_p (aino, mask))
214                 return mask;
215         mask &= ~A_FIBF_DELETE;
216         if (fsdb_mode_representable_p (aino, mask))
217                 return mask;
218         return 0;
219 }
220 
221 /* Return nonzero if we can represent the amigaos_mode of AINO within the
222  * native FS.  Return zero if that is not possible.  */
fsdb_mode_representable_p(const a_inode * aino,int amigaos_mode)223 int fsdb_mode_representable_p (const a_inode *aino, int amigaos_mode)
224 {
225         int mask = amigaos_mode ^ 15;
226 
227         if (0 && aino->dir)
228                 return amigaos_mode == 0;
229 
230         if (mask & A_FIBF_SCRIPT) /* script */
231                 return 0;
232         if ((mask & 15) == 15) /* xxxxRWED == OK */
233                 return 1;
234         if (!(mask & A_FIBF_EXECUTE)) /* not executable */
235                 return 0;
236         if (!(mask & A_FIBF_READ)) /* not readable */
237                 return 0;
238         if ((mask & 15) == (A_FIBF_READ | A_FIBF_EXECUTE)) /* ----RxEx == ReadOnly */
239                 return 1;
240         return 0;
241 }
242 
fsdb_create_unique_nname(a_inode * base,const char * suggestion)243 char *fsdb_create_unique_nname (a_inode *base, const char *suggestion)
244 {
245 	TCHAR *c;
246 	TCHAR tmp[256] = UAEFSDB_BEGINS;
247 	int i;
248 
249 	_tcsncat (tmp, suggestion, 240);
250 
251         /* replace the evil ones... */
252         for (i = 0; i < NUM_EVILCHARS; i++)
253                 while ((c = _tcschr (tmp, evilchars[i])) != 0)
254                         *c = '_';
255 
256         while ((c = _tcschr (tmp, '.')) != 0)
257                 *c = '_';
258         while ((c = _tcschr (tmp, ' ')) != 0)
259                 *c = '_';
260 
261         for (;;) {
262                 TCHAR *p = build_nname (base->nname, tmp);
263                 if (!fsdb_exists (p)) {
264                         write_log (_T("unique name: %s\n"), p);
265                         return p;
266                 }
267                 xfree (p);
268                 /* tmpnam isn't reentrant and I don't really want to hack configure
269                 * right now to see whether tmpnam_r is available...  */
270                 for (i = 0; i < 8; i++) {
271                         tmp[i+8] = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"[rand () % 63];
272                 }
273         }
274 }
275