1/*
2 * Copyright (c) 1996 Regents of The University of Michigan.
3 * All Rights Reserved.  See COPYRIGHT.
4 */
5
6#ifdef HAVE_CONFIG_H
7#include "config.h"
8#endif /* HAVE_CONFIG_H */
9
10#include <stdio.h>
11#include <stdlib.h>
12#ifdef HAVE_UNISTD_H
13#include <unistd.h>
14#endif
15#include <string.h>
16#include <sys/stat.h> /* works around a bug */
17#include <sys/param.h>
18#include <errno.h>
19
20#include <atalk/logger.h>
21#include <atalk/util.h>
22#include <atalk/bstrlib.h>
23#include <atalk/bstradd.h>
24#include <atalk/globals.h>
25#include <atalk/fce_api.h>
26
27#include "volume.h"
28#include "directory.h"
29#include "fork.h"
30
31/* we need to have a hashed list of oforks (by dev inode) */
32#define OFORK_HASHSIZE  64
33static struct ofork *ofork_table[OFORK_HASHSIZE]; /* forks hashed by dev/inode */
34static struct ofork **oforks = NULL;              /* point to allocated table of open forks pointers */
35static int          nforks = 0;
36static u_short      lastrefnum = 0;
37
38
39/* OR some of each character for the hash*/
40static unsigned long hashfn(const struct file_key *key)
41{
42    return key->inode & (OFORK_HASHSIZE - 1);
43}
44
45static void of_hash(struct ofork *of)
46{
47    struct ofork **table;
48
49    table = &ofork_table[hashfn(&of->key)];
50    if ((of->next = *table) != NULL)
51        (*table)->prevp = &of->next;
52    *table = of;
53    of->prevp = table;
54}
55
56static void of_unhash(struct ofork *of)
57{
58    if (of->prevp) {
59        if (of->next)
60            of->next->prevp = of->prevp;
61        *(of->prevp) = of->next;
62    }
63}
64
65int of_flush(const struct vol *vol)
66{
67    int refnum;
68
69    if (!oforks)
70        return 0;
71
72    for ( refnum = 0; refnum < nforks; refnum++ ) {
73        if (oforks[ refnum ] != NULL && (oforks[refnum]->of_vol == vol) &&
74            flushfork( oforks[ refnum ] ) < 0 ) {
75            LOG(log_error, logtype_afpd, "of_flush: %s", strerror(errno) );
76        }
77    }
78    return( 0 );
79}
80
81int of_rename(const struct vol *vol,
82              struct ofork *s_of,
83              struct dir *olddir, const char *oldpath _U_,
84              struct dir *newdir, const char *newpath)
85{
86    struct ofork *of, *next;
87    int done = 0;
88
89    if (!s_of)
90        return AFP_OK;
91
92    next = ofork_table[hashfn(&s_of->key)];
93    while ((of = next)) {
94        next = next->next; /* so we can unhash and still be all right. */
95
96        if (vol == of->of_vol
97            && olddir->d_did == of->of_did
98            && s_of->key.dev == of->key.dev
99            && s_of->key.inode == of->key.inode ) {
100            if (!done) {
101                free(of_name(of));
102                if ((of_name(of) = strdup(newpath)) == NULL)
103                    return AFPERR_MISC;
104                done = 1;
105            }
106            if (newdir != olddir)
107                of->of_did = newdir->d_did;
108        }
109    }
110
111    return AFP_OK;
112}
113
114#define min(a,b)    ((a)<(b)?(a):(b))
115
116struct ofork *
117of_alloc(struct vol *vol,
118         struct dir    *dir,
119         char      *path,
120         uint16_t     *ofrefnum,
121         const int      eid,
122         struct adouble *ad,
123         struct stat    *st)
124{
125    struct ofork        *of;
126    uint16_t       refnum, of_refnum;
127
128    int         i;
129
130    if (!oforks) {
131        nforks = getdtablesize() - 10;
132        /* protect against insane ulimit -n */
133        nforks = min(nforks, 0xffff);
134        oforks = (struct ofork **) calloc(nforks, sizeof(struct ofork *));
135        if (!oforks)
136            return NULL;
137    }
138
139    for ( refnum = ++lastrefnum, i = 0; i < nforks; i++, refnum++ ) {
140        /* cf AFP3.0.pdf, File fork page 40 */
141        if (!refnum)
142            refnum++;
143        if ( oforks[ refnum % nforks ] == NULL ) {
144            break;
145        }
146    }
147    /* grr, Apple and their 'uniquely identifies'
148       the next line is a protection against
149       of_alloc()
150       refnum % nforks = 3
151       lastrefnum = 3
152       oforks[3] != NULL
153       refnum = 4
154       oforks[4] == NULL
155       return 4
156
157       close(oforks[4])
158
159       of_alloc()
160       refnum % nforks = 4
161       ...
162       return 4
163       same if lastrefnum++ rather than ++lastrefnum.
164    */
165    lastrefnum = refnum;
166    if ( i == nforks ) {
167        LOG(log_error, logtype_afpd, "of_alloc: maximum number of forks exceeded.");
168        return( NULL );
169    }
170
171    of_refnum = refnum % nforks;
172    if (( oforks[ of_refnum ] =
173          (struct ofork *)malloc( sizeof( struct ofork ))) == NULL ) {
174        LOG(log_error, logtype_afpd, "of_alloc: malloc: %s", strerror(errno) );
175        return NULL;
176    }
177    of = oforks[of_refnum];
178
179    /* see if we need to allocate space for the adouble struct */
180    if (!ad) {
181        ad = malloc( sizeof( struct adouble ) );
182        if (!ad) {
183            LOG(log_error, logtype_afpd, "of_alloc: malloc: %s", strerror(errno) );
184            free(of);
185            oforks[ of_refnum ] = NULL;
186            return NULL;
187        }
188
189        /* initialize to zero. This is important to ensure that
190           ad_open really does reinitialize the structure. */
191        ad_init(ad, vol);
192        if ((ad->ad_name = strdup(path)) == NULL) {
193            LOG(log_error, logtype_afpd, "of_alloc: malloc: %s", strerror(errno) );
194            free(ad);
195            free(of);
196            oforks[ of_refnum ] = NULL;
197            return NULL;
198        }
199    } else {
200        /* Increase the refcount on this struct adouble. This is
201           decremented again in oforc_dealloc. */
202        ad_ref(ad);
203    }
204
205    of->of_ad = ad;
206    of->of_vol = vol;
207    of->of_did = dir->d_did;
208
209    *ofrefnum = refnum;
210    of->of_refnum = refnum;
211    of->key.dev = st->st_dev;
212    of->key.inode = st->st_ino;
213    if (eid == ADEID_DFORK)
214        of->of_flags = AFPFORK_DATA;
215    else
216        of->of_flags = AFPFORK_RSRC;
217
218    of_hash(of);
219    return( of );
220}
221
222struct ofork *of_find(const uint16_t ofrefnum )
223{
224    if (!oforks || !nforks)
225        return NULL;
226
227    return( oforks[ ofrefnum % nforks ] );
228}
229
230/* -------------------------- */
231int of_stat(const struct vol *vol, struct path *path)
232{
233    int ret;
234
235    path->st_errno = 0;
236    path->st_valid = 1;
237
238    if ((ret = ostat(path->u_name, &path->st, vol_syml_opt(vol))) < 0) {
239        LOG(log_debug, logtype_afpd, "of_stat('%s/%s': %s)",
240            cfrombstr(curdir->d_fullpath), path->u_name, strerror(errno));
241    	path->st_errno = errno;
242    }
243
244    return ret;
245}
246
247
248#ifdef HAVE_ATFUNCS
249int of_fstatat(int dirfd, struct path *path)
250{
251    int ret;
252
253    path->st_errno = 0;
254    path->st_valid = 1;
255
256    if ((ret = fstatat(dirfd, path->u_name, &path->st, AT_SYMLINK_NOFOLLOW)) < 0)
257    	path->st_errno = errno;
258
259   return ret;
260}
261#endif /* HAVE_ATFUNCS */
262
263/* --------------------------
264   stat the current directory.
265   stat(".") works even if "." is deleted thus
266   we have to stat ../name because we want to know if it's there
267*/
268int of_statdir(struct vol *vol, struct path *path)
269{
270    static char pathname[ MAXPATHLEN + 1] = "../";
271    int ret;
272    size_t len;
273    struct dir *dir;
274
275    if (*path->m_name) {
276        /* not curdir */
277        return of_stat(vol, path);
278    }
279    path->st_errno = 0;
280    path->st_valid = 1;
281    /* FIXME, what about: we don't have r-x perm anymore ? */
282    len = blength(path->d_dir->d_u_name);
283    if (len > (MAXPATHLEN - 3))
284        len = MAXPATHLEN - 3;
285    strncpy(pathname + 3, cfrombstr(path->d_dir->d_u_name), len + 1);
286
287    LOG(log_debug, logtype_afpd, "of_statdir: stating: '%s'", pathname);
288
289    if (!(ret = ostat(pathname, &path->st, vol_syml_opt(vol))))
290        return 0;
291
292    path->st_errno = errno;
293
294    /* hmm, can't stat curdir anymore */
295    if (errno == EACCES && (dir = dirlookup(vol, curdir->d_pdid))) {
296       if (movecwd(vol, dir))
297           return -1;
298       path->st_errno = 0;
299
300       if ((ret = ostat(cfrombstr(path->d_dir->d_u_name), &path->st, vol_syml_opt(vol))) < 0)
301           path->st_errno = errno;
302    }
303
304    return ret;
305}
306
307/* -------------------------- */
308struct ofork *of_findname(const struct vol *vol, struct path *path)
309{
310    struct ofork *of;
311    struct file_key key;
312
313    if (!path->st_valid) {
314        of_stat(vol, path);
315    }
316
317    if (path->st_errno)
318        return NULL;
319
320    key.dev = path->st.st_dev;
321    key.inode = path->st.st_ino;
322
323    for (of = ofork_table[hashfn(&key)]; of; of = of->next) {
324        if (key.dev == of->key.dev && key.inode == of->key.inode ) {
325            return of;
326        }
327    }
328
329    return NULL;
330}
331
332/*!
333 * @brief Search for open fork by dirfd/name
334 *
335 * Function call of_fstatat with dirfd and path and uses dev and ino
336 * to search the open fork table.
337 *
338 * @param dirfd     (r) directory fd
339 * @param path      (rw) pointer to struct path
340 */
341#ifdef HAVE_ATFUNCS
342struct ofork *of_findnameat(int dirfd, struct path *path)
343{
344    struct ofork *of;
345    struct file_key key;
346
347    if ( ! path->st_valid) {
348        of_fstatat(dirfd, 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#endif
366
367void of_dealloc(struct ofork *of)
368{
369    if (!oforks)
370        return;
371
372    of_unhash(of);
373    oforks[ of->of_refnum % nforks ] = NULL;
374
375    /* decrease refcount */
376    of->of_ad->ad_refcount--;
377
378    if ( of->of_ad->ad_refcount <= 0) {
379        free( of->of_ad->ad_name );
380        free( of->of_ad);
381    }
382
383    free( of );
384}
385
386/* --------------------------- */
387int of_closefork(const AFPObj *obj, struct ofork *ofork)
388{
389    struct timeval      tv;
390    int         adflags = 0;
391    int                 ret;
392
393    adflags = 0;
394    if (ofork->of_flags & AFPFORK_DATA)
395        adflags |= ADFLAGS_DF;
396    if (ofork->of_flags & AFPFORK_META)
397        adflags |= ADFLAGS_HF;
398    if (ofork->of_flags & AFPFORK_RSRC) {
399        adflags |= ADFLAGS_RF;
400        /* Only set the rfork's length if we're closing the rfork. */
401        ad_refresh(NULL, ofork->of_ad );
402        if ((ofork->of_flags & AFPFORK_DIRTY) && !gettimeofday(&tv, NULL)) {
403            ad_setdate(ofork->of_ad, AD_DATE_MODIFY | AD_DATE_UNIX,tv.tv_sec);
404            ad_flush( ofork->of_ad );
405        }
406    }
407
408    /* Somone has used write_fork, we assume file was changed, register it to file change event api */
409    if (ofork->of_flags & AFPFORK_MODIFIED) {
410        struct dir *dir =  dirlookup(ofork->of_vol, ofork->of_did);
411        bstring forkpath = bformat("%s/%s", bdata(dir->d_fullpath), of_name(ofork));
412        fce_register(FCE_FILE_MODIFY, bdata(forkpath), NULL, fce_file);
413        bdestroy(forkpath);
414    }
415
416    ad_unlock(ofork->of_ad, ofork->of_refnum, ofork->of_flags & AFPFORK_ERROR ? 0 : 1);
417
418#ifdef HAVE_FSHARE_T
419    if (obj->options.flags & OPTION_SHARE_RESERV) {
420        fshare_t shmd;
421        shmd.f_id = ofork->of_refnum;
422        if (AD_DATA_OPEN(ofork->of_ad))
423            fcntl(ad_data_fileno(ofork->of_ad), F_UNSHARE, &shmd);
424        if (AD_RSRC_OPEN(ofork->of_ad))
425            fcntl(ad_reso_fileno(ofork->of_ad), F_UNSHARE, &shmd);
426    }
427#endif
428
429    ret = 0;
430    if ( ad_close( ofork->of_ad, adflags | ADFLAGS_SETSHRMD) < 0 ) {
431        ret = -1;
432    }
433
434    of_dealloc(ofork);
435
436    return ret;
437}
438
439/* ----------------------
440
441 */
442struct adouble *of_ad(const struct vol *vol, struct path *path, struct adouble *ad)
443{
444    struct ofork        *of;
445    struct adouble      *adp;
446
447    if ((of = of_findname(vol, path))) {
448        adp = of->of_ad;
449    } else {
450        ad_init(ad, vol);
451        adp = ad;
452    }
453    return adp;
454}
455
456/* ----------------------
457   close all forks for a volume
458*/
459void of_closevol(const AFPObj *obj, const struct vol *vol)
460{
461    int refnum;
462
463    if (!oforks)
464        return;
465
466    for ( refnum = 0; refnum < nforks; refnum++ ) {
467        if (oforks[ refnum ] != NULL && oforks[refnum]->of_vol == vol) {
468            if (of_closefork(obj, oforks[ refnum ]) < 0 ) {
469                LOG(log_error, logtype_afpd, "of_closevol: %s", strerror(errno) );
470            }
471        }
472    }
473    return;
474}
475
476/* ----------------------
477   close all forks for a volume
478*/
479void of_close_all_forks(const AFPObj *obj)
480{
481    int refnum;
482
483    if (!oforks)
484        return;
485
486    for ( refnum = 0; refnum < nforks; refnum++ ) {
487        if (oforks[ refnum ] != NULL) {
488            if (of_closefork(obj, oforks[ refnum ]) < 0 ) {
489                LOG(log_error, logtype_afpd, "of_close_all_forks: %s", strerror(errno) );
490            }
491        }
492    }
493    return;
494}
495
496