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   *
7   * Copyright 1999 Bernd Schmidt
8   */
9 
10 #include "sysconfig.h"
11 #include "sysdeps.h"
12 
13 #include "threaddep/thread.h"
14 #include "options.h"
15 #include "uae.h"
16 #include "memory.h"
17 #include "custom.h"
18 #include "newcpu.h"
19 #include "filesys.h"
20 #include "autoconf.h"
21 #include "fsusage.h"
22 #include "scsidev.h"
23 #include "fsdb.h"
24 
25 /* The on-disk format is as follows:
26  * Offset 0, 1 byte, valid
27  * Offset 1, 4 bytes, mode
28  * Offset 5, 257 bytes, aname
29  * Offset 263, 257 bytes, nname
30  * Offset 518, 81 bytes, comment
31  */
32 
nname_begin(char * nname)33 char *nname_begin (char *nname)
34 {
35     char *p = strrchr (nname, FSDB_DIR_SEPARATOR);
36     if (p)
37 	return p + 1;
38     return nname;
39 }
40 
41 /* Find the name REL in directory DIRNAME.  If we find a file that
42  * has exactly the same name, return REL.  If we find a file that
43  * has the same name when compared case-insensitively, return a
44  * malloced string that contains the name we found.  If no file
45  * exists that compares equal to REL, return 0.  */
fsdb_search_dir(const char * dirname,char * rel)46 char *fsdb_search_dir (const char *dirname, char *rel)
47 {
48     char *p = 0;
49     struct dirent *de;
50     DIR *dir = opendir (dirname);
51 
52     /* This really shouldn't happen...  */
53     if (! dir)
54 	return 0;
55 
56     while (p == 0 && (de = readdir (dir)) != 0) {
57 	if (strcmp (de->d_name, rel) == 0)
58 	    p = rel;
59 	else if (strcasecmp (de->d_name, rel) == 0)
60 	    p = my_strdup (de->d_name);
61     }
62     closedir (dir);
63     return p;
64 }
65 
get_fsdb(a_inode * dir,const char * mode)66 static FILE *get_fsdb (a_inode *dir, const char *mode)
67 {
68     char *n = build_nname (dir->nname, FSDB_FILE);
69     FILE *f = fopen (n, mode);
70     free (n);
71     return f;
72 }
73 
kill_fsdb(a_inode * dir)74 static void kill_fsdb (a_inode *dir)
75 {
76     char *n = build_nname (dir->nname, FSDB_FILE);
77     unlink (n);
78     free (n);
79 }
80 
81 /* Prune the db file the first time this directory is opened in a session.  */
fsdb_clean_dir(a_inode * dir)82 void fsdb_clean_dir (a_inode *dir)
83 {
84     char buf[1 + 4 + 257 + 257 + 81];
85     char *n = build_nname (dir->nname, FSDB_FILE);
86     FILE *f = fopen (n, "r+b");
87     off_t pos1 = 0, pos2 = 0;
88 
89     if (f == 0)
90 	return;
91     for (;; pos2 += sizeof buf) {
92 	if (fread (buf, 1, sizeof buf, f) < sizeof buf)
93 	    break;
94 	if (buf[0] == 0)
95 	    continue;
96 	if (pos1 != pos2) {
97 	    fseek (f, pos1, SEEK_SET);
98 	    fwrite (buf, 1, sizeof buf, f);
99 	    fseek (f, pos2 + sizeof buf, SEEK_SET);
100 	}
101 	pos1 += sizeof buf;
102     }
103     fclose (f);
104     truncate (n, pos1);
105     free (n);
106 }
107 
aino_from_buf(a_inode * base,char * buf,long off)108 static a_inode *aino_from_buf (a_inode *base, char *buf, long off)
109 {
110     uae_u32 mode;
111     a_inode *aino = (a_inode *) xmalloc (sizeof (a_inode));
112 
113     mode = do_get_mem_long ((uae_u32 *)(buf + 1));
114     buf += 5;
115     aino->aname = my_strdup (buf);
116     buf += 257;
117     aino->nname = build_nname (base->nname, buf);
118     buf += 257;
119     aino->comment = *buf != '\0' ? my_strdup (buf) : 0;
120     fsdb_fill_file_attrs (aino);
121     aino->amigaos_mode = mode;
122     aino->has_dbentry = 1;
123     aino->dirty = 0;
124     aino->db_offset = off;
125     return aino;
126 }
127 
fsdb_lookup_aino_aname(a_inode * base,const char * aname)128 a_inode *fsdb_lookup_aino_aname (a_inode *base, const char *aname)
129 {
130     FILE *f = get_fsdb (base, "rb");
131     long off = 0;
132 
133     if (f == 0)
134 	return 0;
135 
136     for (;;) {
137 	char buf[1 + 4 + 257 + 257 + 81];
138 	if (fread (buf, 1, sizeof buf, f) < sizeof buf)
139 	    break;
140 	if (buf[0] != 0 && same_aname (buf + 5, aname)) {
141 	    fclose (f);
142 	    return aino_from_buf (base, buf, off);
143 	}
144 	off += sizeof buf;
145     }
146     fclose (f);
147     return 0;
148 }
149 
fsdb_lookup_aino_nname(a_inode * base,const char * nname)150 a_inode *fsdb_lookup_aino_nname (a_inode *base, const char *nname)
151 {
152     FILE *f = get_fsdb (base, "rb");
153     long off = 0;
154 
155     if (f == 0)
156 	return 0;
157 
158     for (;;) {
159 	char buf[1 + 4 + 257 + 257 + 81];
160 	if (fread (buf, 1, sizeof buf, f) < sizeof buf)
161 	    break;
162 	if (buf[0] != 0 && strcmp (buf + 5 + 257, nname) == 0) {
163 	    fclose (f);
164 	    return aino_from_buf (base, buf, off);
165 	}
166 	off += sizeof buf;
167     }
168     fclose (f);
169     return 0;
170 }
171 
fsdb_used_as_nname(a_inode * base,const char * nname)172 int fsdb_used_as_nname (a_inode *base, const char *nname)
173 {
174     FILE *f = get_fsdb (base, "rb");
175     if (f == 0)
176 	return 0;
177     for (;;) {
178 	char buf[1 + 4 + 257 + 257 + 81];
179 	if (fread (buf, 1, sizeof buf, f) < sizeof buf)
180 	    break;
181 	if (buf[0] == 0)
182 	    continue;
183 	if (strcmp (buf + 5 + 257, nname) == 0) {
184 	    fclose (f);
185 	    return 1;
186 	}
187     }
188     fclose (f);
189     return 0;
190 }
191 
needs_dbentry(a_inode * aino)192 static int needs_dbentry (a_inode *aino)
193 {
194     const char *an_begin, *nn_begin;
195 
196     if (aino->deleted)
197 	return 0;
198 
199     if (! fsdb_mode_representable_p (aino) || aino->comment != 0)
200 	return 1;
201 
202     nn_begin = nname_begin (aino->nname);
203     return strcmp (nn_begin, aino->aname) != 0;
204 }
205 
write_aino(FILE * f,a_inode * aino)206 static void write_aino (FILE *f, a_inode *aino)
207 {
208     char buf[1 + 4 + 257 + 257 + 81];
209     buf[0] = aino->needs_dbentry;
210     do_put_mem_long ((uae_u32 *)(buf + 1), aino->amigaos_mode);
211     strncpy (buf + 5, aino->aname, 256);
212     buf[5 + 256] = '\0';
213     strncpy (buf + 5 + 257, nname_begin (aino->nname), 256);
214     buf[5 + 257 + 256] = '\0';
215     strncpy (buf + 5 + 2*257, aino->comment ? aino->comment : "", 80);
216     buf[5 + 2 * 257 + 80] = '\0';
217     fwrite (buf, 1, sizeof buf, f);
218     aino->has_dbentry = aino->needs_dbentry;
219 }
220 
221 /* Write back the db file for a directory.  */
222 
fsdb_dir_writeback(a_inode * dir)223 void fsdb_dir_writeback (a_inode *dir)
224 {
225     FILE *f;
226     int changes_needed = 0;
227     int entries_needed = 0;
228     a_inode *aino;
229 
230     /* First pass: clear dirty bits where unnecessary, and see if any work
231      * needs to be done.  */
232     for (aino = dir->child; aino; aino = aino->sibling) {
233 	int old_needs_dbentry = aino->needs_dbentry;
234 	aino->needs_dbentry = old_needs_dbentry;
235 	if (! aino->dirty)
236 	    continue;
237 	aino->needs_dbentry = needs_dbentry (aino);
238 	entries_needed |= aino->needs_dbentry;
239 	if (! aino->needs_dbentry && ! old_needs_dbentry)
240 	    aino->dirty = 0;
241 	else
242 	    changes_needed = 1;
243     }
244     if (! entries_needed) {
245 	kill_fsdb (dir);
246 	return;
247     }
248 
249     if (! changes_needed)
250 	return;
251 
252     f = get_fsdb (dir, "r+b");
253     if (f == 0) {
254 	f = get_fsdb (dir, "w+b");
255 	if (f == 0)
256 	    /* This shouldn't happen... */
257 	    return;
258     }
259 
260     for (aino = dir->child; aino; aino = aino->sibling) {
261 	off_t pos;
262 
263 	if (! aino->dirty)
264 	    continue;
265 	aino->dirty = 0;
266 
267 	if (! aino->has_dbentry) {
268 	    aino->db_offset = fseek (f, 0, SEEK_END);
269 	    aino->has_dbentry = 1;
270 	} else
271 	    fseek (f, aino->db_offset, SEEK_SET);
272 
273 	write_aino (f, aino);
274     }
275     fclose (f);
276 }
277