]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/ofork.c
commit dircache rewrite.
[netatalk.git] / etc / afpd / ofork.c
1 /*
2  * $Id: ofork.c,v 1.30.4.1 2010-02-01 10:56:08 franklahm Exp $
3  *
4  * Copyright (c) 1996 Regents of The University of Michigan.
5  * All Rights Reserved.  See COPYRIGHT.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif /* HAVE_CONFIG_H */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #ifdef HAVE_UNISTD_H
15 #include <unistd.h>
16 #endif
17 #include <string.h>
18 #include <sys/stat.h> /* works around a bug */
19 #include <sys/param.h>
20 #include <errno.h>
21
22 #include <atalk/logger.h>
23 #include <atalk/util.h>
24 #include <atalk/bstrlib.h>
25 #include <atalk/bstradd.h>
26
27 #include "globals.h"
28 #include "volume.h"
29 #include "directory.h"
30 #include "fork.h"
31
32 /* we need to have a hashed list of oforks (by dev inode). just hash
33  * by first letter. */
34 #define OFORK_HASHSIZE  64
35 static struct ofork     *ofork_table[OFORK_HASHSIZE];
36
37 static struct ofork     **oforks = NULL;
38 static int              nforks = 0;
39 static u_short          lastrefnum = 0;
40
41
42 /* OR some of each character for the hash*/
43 static unsigned long hashfn(const struct file_key *key)
44 {
45 #if 0
46     unsigned long i = 0;
47     while (*name) {
48         i = ((i << 4) | (8*sizeof(i) - 4)) ^ *name++;
49     }
50 #endif    
51     return key->inode & (OFORK_HASHSIZE - 1);
52 }
53
54 static void of_hash(struct ofork *of)
55 {
56     struct ofork **table;
57
58     table = &ofork_table[hashfn(&of->key)];
59     if ((of->next = *table) != NULL)
60         (*table)->prevp = &of->next;
61     *table = of;
62     of->prevp = table;
63 }
64
65 static void of_unhash(struct ofork *of)
66 {
67     if (of->prevp) {
68         if (of->next)
69             of->next->prevp = of->prevp;
70         *(of->prevp) = of->next;
71     }
72 }
73
74 #ifdef DEBUG1
75 void of_pforkdesc( FILE *f)
76 {
77     int ofrefnum;
78
79     if (!oforks)
80         return;
81
82     for ( ofrefnum = 0; ofrefnum < nforks; ofrefnum++ ) {
83         if ( oforks[ ofrefnum ] != NULL ) {
84             fprintf( f, "%hu <%s>\n", ofrefnum, of_name(oforks[ ofrefnum ]));
85         }
86     }
87 }
88 #endif
89
90 int of_flush(const struct vol *vol)
91 {
92     int refnum;
93
94     if (!oforks)
95         return 0;
96
97     for ( refnum = 0; refnum < nforks; refnum++ ) {
98         if (oforks[ refnum ] != NULL && (oforks[refnum]->of_vol == vol) &&
99                 flushfork( oforks[ refnum ] ) < 0 ) {
100             LOG(log_error, logtype_afpd, "of_flush: %s", strerror(errno) );
101         }
102     }
103     return( 0 );
104 }
105
106 int of_rename(
107     const struct vol *vol,
108     struct ofork *s_of,
109     struct dir *olddir, const char *oldpath _U_,
110     struct dir *newdir, const char *newpath)
111 {
112     struct ofork *of, *next, *d_ofork;
113     int done = 0;
114
115     if (!s_of)
116         return AFP_OK;
117         
118     next = ofork_table[hashfn(&s_of->key)];
119     while ((of = next)) {
120         next = next->next; /* so we can unhash and still be all right. */
121
122         if (vol == of->of_vol && olddir == of->of_dir &&
123                  s_of->key.dev == of->key.dev && 
124                  s_of->key.inode == of->key.inode ) {
125             if (!done) {
126                 strlcpy( of_name(of), newpath, of->of_ad->ad_m_namelen);
127                 done = 1;
128             }
129             if (newdir != olddir) {
130                 of->of_d_prev->of_d_next = of->of_d_next;
131                 of->of_d_next->of_d_prev = of->of_d_prev;
132                 if (of->of_dir->d_ofork == of) {
133                     of->of_dir->d_ofork = (of == of->of_d_next) ? NULL : of->of_d_next;
134                 }           
135                 of->of_dir = newdir;
136                 if (!(d_ofork = newdir->d_ofork)) {
137                     newdir->d_ofork = of;
138                     of->of_d_next = of->of_d_prev = of;
139                 } else {
140                     of->of_d_next = d_ofork;
141                     of->of_d_prev = d_ofork->of_d_prev;
142                     of->of_d_prev->of_d_next = of;
143                     d_ofork->of_d_prev = of;
144                 }
145             }
146         }
147     }
148
149     return AFP_OK;
150 }
151
152 #define min(a,b)        ((a)<(b)?(a):(b))
153
154 struct ofork *
155 of_alloc(struct vol *vol,
156     struct dir     *dir,
157     char           *path,
158     u_int16_t      *ofrefnum,
159     const int      eid,
160     struct adouble *ad,
161     struct stat    *st)
162 {
163     struct ofork        *of, *d_ofork;
164     u_int16_t           refnum, of_refnum;
165
166     int                 i;
167
168     if (!oforks) {
169         nforks = getdtablesize() - 10;
170         /* protect against insane ulimit -n */
171         nforks = min(nforks, 0xffff);
172         oforks = (struct ofork **) calloc(nforks, sizeof(struct ofork *));
173         if (!oforks)
174             return NULL;
175     }
176
177     for ( refnum = ++lastrefnum, i = 0; i < nforks; i++, refnum++ ) {
178         /* cf AFP3.0.pdf, File fork page 40 */
179         if (!refnum)
180             refnum++;
181         if ( oforks[ refnum % nforks ] == NULL ) {
182             break;
183         }
184     }
185     /* grr, Apple and their 'uniquely identifies'
186           the next line is a protection against 
187           of_alloc()
188              refnum % nforks = 3 
189              lastrefnum = 3
190              oforks[3] != NULL 
191              refnum = 4
192              oforks[4] == NULL
193              return 4
194          
195           close(oforks[4])
196       
197           of_alloc()
198              refnum % nforks = 4
199              ...
200              return 4
201          same if lastrefnum++ rather than ++lastrefnum. 
202     */
203     lastrefnum = refnum;
204     if ( i == nforks ) {
205         LOG(log_error, logtype_afpd, "of_alloc: maximum number of forks exceeded.");
206         return( NULL );
207     }
208
209     of_refnum = refnum % nforks;
210     if (( oforks[ of_refnum ] =
211                 (struct ofork *)malloc( sizeof( struct ofork ))) == NULL ) {
212         LOG(log_error, logtype_afpd, "of_alloc: malloc: %s", strerror(errno) );
213         return NULL;
214     }
215     of = oforks[of_refnum];
216
217     /* see if we need to allocate space for the adouble struct */
218     if (!ad) {
219         ad = malloc( sizeof( struct adouble ) );
220         if (!ad) {
221             LOG(log_error, logtype_afpd, "of_alloc: malloc: %s", strerror(errno) );
222             free(of);
223             oforks[ of_refnum ] = NULL;
224             return NULL;
225         }
226
227         /* initialize to zero. This is important to ensure that
228            ad_open really does reinitialize the structure. */
229         ad_init(ad, vol->v_adouble, vol->v_ad_options);
230
231         ad->ad_m_namelen = 255 +1;
232         /* here's the deal: we allocate enough for the standard mac file length.
233          * in the future, we'll reallocate in fairly large jumps in case
234          * of long unicode names */
235         if (( ad->ad_m_name =(char *)malloc( ad->ad_m_namelen )) == NULL ) {
236             LOG(log_error, logtype_afpd, "of_alloc: malloc: %s", strerror(errno) );
237             free(ad);
238             free(of);
239             oforks[ of_refnum ] = NULL;
240             return NULL;
241         }
242         strlcpy( ad->ad_m_name, path, ad->ad_m_namelen);
243     } else {
244         /* Increase the refcount on this struct adouble. This is
245            decremented again in oforc_dealloc. */
246         ad->ad_refcount++;
247     }
248
249     of->of_ad = ad;
250     of->of_vol = vol;
251     of->of_dir = dir;
252
253     if (!(d_ofork = dir->d_ofork)) {
254         dir->d_ofork = of;
255         of->of_d_next = of->of_d_prev = of;
256     } else {
257         of->of_d_next = d_ofork;
258         of->of_d_prev = d_ofork->of_d_prev;
259         d_ofork->of_d_prev->of_d_next = of;
260         d_ofork->of_d_prev = of;
261     }
262
263     *ofrefnum = refnum;
264     of->of_refnum = refnum;
265     of->key.dev = st->st_dev;
266     of->key.inode = st->st_ino;
267     if (eid == ADEID_DFORK)
268         of->of_flags = AFPFORK_DATA;
269     else
270         of->of_flags = AFPFORK_RSRC;
271
272     of_hash(of);
273     return( of );
274 }
275
276 struct ofork *of_find(const u_int16_t ofrefnum )
277 {
278     if (!oforks || !nforks)
279         return NULL;
280
281     return( oforks[ ofrefnum % nforks ] );
282 }
283
284 /* -------------------------- */
285 int of_stat(struct path *path)
286 {
287     int ret;
288
289     LOG(log_maxdebug, logtype_afpd, "of_stat: {stat:'%s/%s'}", getcwdpath(), path->u_name);
290
291     path->st_errno = 0;
292     path->st_valid = 1;
293     if ((ret = stat(path->u_name, &path->st)) < 0)
294         path->st_errno = errno;
295     return ret;
296 }
297
298 /* -------------------------- 
299    stat the current directory.
300    stat(".") works even if "." is deleted thus
301    we have to stat ../name because we want to know if it's there
302 */
303 int of_statdir(struct vol *vol, struct path *path)
304 {
305     static char pathname[ MAXPATHLEN + 1] = "../";
306     int ret;
307     size_t len;
308     struct dir *dir;
309
310     if (*path->m_name) {
311         /* not curdir */
312         return of_stat (path);
313     }
314     path->st_errno = 0;
315     path->st_valid = 1;
316     /* FIXME, what about: we don't have r-x perm anymore ? */
317     len = blength(path->d_dir->d_u_name);
318     if (len > (MAXPATHLEN - 3))
319         len = MAXPATHLEN - 3;
320     strncpy(pathname + 3, cfrombstring(path->d_dir->d_u_name), len + 1);
321
322     LOG(log_debug, logtype_afpd, "of_statdir: stating: '%s'", pathname);
323
324     if (!(ret = stat(pathname, &path->st)))
325         return 0;
326         
327     path->st_errno = errno;
328
329     /* hmm, can't stat curdir anymore */
330     if (errno == EACCES && (dir = dirlookup(vol, curdir->d_pdid))) {
331        if (movecwd(vol, dir)) 
332            return -1;
333        path->st_errno = 0;
334        if ((ret = stat(cfrombstring(path->d_dir->d_u_name), &path->st)) < 0) 
335            path->st_errno = errno;
336     }
337
338     return ret;
339 }
340
341 /* -------------------------- */
342 struct ofork *of_findname(struct path *path)
343 {
344     struct ofork *of;
345     struct file_key key;
346     
347     if (!path->st_valid) {
348        of_stat(path);
349     }
350         
351     if (path->st_errno)
352         return NULL;
353
354     key.dev = path->st.st_dev;
355     key.inode = path->st.st_ino;
356
357     for (of = ofork_table[hashfn(&key)]; of; of = of->next) {
358         if (key.dev == of->key.dev && key.inode == of->key.inode ) {
359             return of;
360         }
361     }
362
363     return NULL;
364 }
365
366 void of_dealloc( struct ofork *of)
367 {
368     if (!oforks)
369         return;
370
371     of_unhash(of);
372
373     /* detach ofork */
374     of->of_d_prev->of_d_next = of->of_d_next;
375     of->of_d_next->of_d_prev = of->of_d_prev;
376     if (of->of_dir->d_ofork == of) {
377         of->of_dir->d_ofork = (of == of->of_d_next) ? NULL : of->of_d_next;
378     }
379
380     oforks[ of->of_refnum % nforks ] = NULL;
381
382     /* decrease refcount */
383     of->of_ad->ad_refcount--;
384
385     if ( of->of_ad->ad_refcount <= 0) {
386         free( of->of_ad->ad_m_name );
387         free( of->of_ad);
388     } else {/* someone's still using it. just free this user's locks */
389         ad_unlock(of->of_ad, of->of_refnum);
390     }
391
392     free( of );
393 }
394
395 /* --------------------------- */
396 int of_closefork(struct ofork *ofork)
397 {
398     struct timeval      tv;
399     int                 adflags, doflush = 0;
400     int                 ret;
401
402     adflags = 0;
403     if ((ofork->of_flags & AFPFORK_DATA) && (ad_data_fileno( ofork->of_ad ) != -1)) {
404             adflags |= ADFLAGS_DF;
405     }
406     if ( (ofork->of_flags & AFPFORK_OPEN) && ad_reso_fileno( ofork->of_ad ) != -1 ) {
407         adflags |= ADFLAGS_HF;
408         /*
409          * Only set the rfork's length if we're closing the rfork.
410          */
411         if ((ofork->of_flags & AFPFORK_RSRC)) {
412             ad_refresh( ofork->of_ad );
413             if ((ofork->of_flags & AFPFORK_DIRTY) && !gettimeofday(&tv, NULL)) {
414                 ad_setdate(ofork->of_ad, AD_DATE_MODIFY | AD_DATE_UNIX,tv.tv_sec);
415                 doflush++;
416             }
417             if ( doflush ) {
418                  ad_flush( ofork->of_ad );
419             }
420         }
421     }
422     ret = 0;
423     if ( ad_close( ofork->of_ad, adflags ) < 0 ) {
424         ret = -1;
425     }
426  
427     of_dealloc( ofork );
428     return ret;
429 }
430
431 /* ----------------------
432
433 */
434 struct adouble *of_ad(const struct vol *vol, struct path *path, struct adouble *ad)
435 {
436     struct ofork        *of;
437     struct adouble      *adp;
438
439     if ((of = of_findname(path))) {
440         adp = of->of_ad;
441     } else {
442         ad_init(ad, vol->v_adouble, vol->v_ad_options);
443         adp = ad;
444     }
445     return adp;
446 }
447
448 /* ---------------------- 
449    close all forks for a volume
450 */
451 void of_closevol(const struct vol *vol)
452 {
453     int refnum;
454
455     if (!oforks)
456         return;
457
458     for ( refnum = 0; refnum < nforks; refnum++ ) {
459         if (oforks[ refnum ] != NULL && oforks[refnum]->of_vol == vol) {
460             if (of_closefork( oforks[ refnum ]) < 0 ) {
461                 LOG(log_error, logtype_afpd, "of_closevol: %s", strerror(errno) );
462             }
463         }
464     }
465     return;
466 }
467