• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/timemachine/netatalk-2.2.0/libatalk/vfs/
1/*
2 * Copyright (c) 1990,1993 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 <unistd.h>
11#include <errno.h>
12#include <stdlib.h>
13#include <sys/param.h>
14#include <sys/types.h>
15#include <sys/stat.h>
16#include <string.h>
17
18#include <atalk/afp.h>
19#include <atalk/util.h>
20#include <atalk/directory.h>
21#include <atalk/volume.h>
22#include <atalk/logger.h>
23#include <atalk/unix.h>
24#include <atalk/acl.h>
25
26/* -----------------------------
27   a dropbox is a folder where w is set but not r eg:
28   rwx-wx-wx or rwx-wx--
29   rwx----wx (is not asked by a Mac with OS >= 8.0 ?)
30*/
31int stickydirmode(const char *name, const mode_t mode, const int dropbox, const mode_t v_umask)
32{
33    int retval = 0;
34
35#ifdef DROPKLUDGE
36    /* Turn on the sticky bit if this is a drop box, also turn off the setgid bit */
37    if ((dropbox & AFPVOL_DROPBOX)) {
38        int uid;
39
40        if ( ( (mode & S_IWOTH) && !(mode & S_IROTH)) ||
41             ( (mode & S_IWGRP) && !(mode & S_IRGRP)) )
42        {
43            uid=geteuid();
44            if ( seteuid(0) < 0) {
45                LOG(log_error, logtype_afpd, "stickydirmode: unable to seteuid root: %s", strerror(errno));
46            }
47            if ( (retval=chmod( name, ( (DIRBITS | mode | S_ISVTX) & ~v_umask) )) < 0) {
48                LOG(log_error, logtype_afpd, "stickydirmode: chmod \"%s\": %s", fullpathname(name), strerror(errno) );
49            } else {
50                LOG(log_debug, logtype_afpd, "stickydirmode: chmod \"%s\": %s", fullpathname(name), strerror(retval) );
51            }
52            seteuid(uid);
53            return retval;
54        }
55    }
56#endif /* DROPKLUDGE */
57
58    /*
59     *  Ignore EPERM errors:  We may be dealing with a directory that is
60     *  group writable, in which case chmod will fail.
61     */
62    if ( (chmod( name, (DIRBITS | mode) & ~v_umask ) < 0) && errno != EPERM &&
63         !(errno == ENOENT && (dropbox & AFPVOL_NOADOUBLE)) )
64    {
65        LOG(log_error, logtype_afpd, "stickydirmode: chmod \"%s\": %s", fullpathname(name), strerror(errno) );
66        retval = -1;
67    }
68
69    return retval;
70}
71
72/* ------------------------- */
73int dir_rx_set(mode_t mode)
74{
75    return (mode & (S_IXUSR | S_IRUSR)) == (S_IXUSR | S_IRUSR);
76}
77
78/* --------------------- */
79int setfilmode(const char * name, mode_t mode, struct stat *st, mode_t v_umask)
80{
81    struct stat sb;
82    mode_t mask = S_IRWXU | S_IRWXG | S_IRWXO;  /* rwx for owner group and other, by default */
83
84    if (!st) {
85        if (lstat(name, &sb) != 0)
86            return -1;
87        st = &sb;
88    }
89
90    if (S_ISLNK(st->st_mode))
91        return 0; /* we don't want to change link permissions */
92
93    mode |= st->st_mode & ~mask; /* keep other bits from previous mode */
94
95    if ( chmod( name,  mode & ~v_umask ) < 0 && errno != EPERM ) {
96        return -1;
97    }
98    return 0;
99}
100
101/*
102 * @brief system rmdir with afp error code.
103 *
104 * Supports *at semantics (cf openat) if HAVE_ATFUNCS. Pass dirfd=-1 to ignore this.
105 */
106int netatalk_rmdir_all_errors(int dirfd, const char *name)
107{
108    int err;
109
110#ifdef HAVE_ATFUNCS
111    if (dirfd == -1)
112        dirfd = AT_FDCWD;
113    err = unlinkat(dirfd, name, AT_REMOVEDIR);
114#else
115    err = rmdir(name);
116#endif
117
118    if (err < 0) {
119        switch ( errno ) {
120        case ENOENT :
121            return AFPERR_NOOBJ;
122        case ENOTEMPTY :
123            return AFPERR_DIRNEMPT;
124        case EPERM:
125        case EACCES :
126            return AFPERR_ACCESS;
127        case EROFS:
128            return AFPERR_VLOCK;
129        default :
130            return AFPERR_PARAM;
131        }
132    }
133    return AFP_OK;
134}
135
136/*
137 * @brief System rmdir with afp error code, but ENOENT is not an error.
138 *
139 * Supports *at semantics (cf openat) if HAVE_ATFUNCS. Pass dirfd=-1 to ignore this.
140 */
141int netatalk_rmdir(int dirfd, const char *name)
142{
143    int ret = netatalk_rmdir_all_errors(dirfd, name);
144    if (ret == AFPERR_NOOBJ)
145        return AFP_OK;
146    return ret;
147}
148
149/* -------------------
150   system unlink with afp error code.
151   ENOENT is not an error.
152*/
153int netatalk_unlink(const char *name)
154{
155    if (unlink(name) < 0) {
156        switch (errno) {
157        case ENOENT :
158            break;
159        case EROFS:
160            return AFPERR_VLOCK;
161        case EPERM:
162        case EACCES :
163            return AFPERR_ACCESS;
164        default :
165            return AFPERR_PARAM;
166        }
167    }
168    return AFP_OK;
169}
170
171char *fullpathname(const char *name)
172{
173    static char wd[ MAXPATHLEN + 1];
174
175    if ( getcwd( wd , MAXPATHLEN) ) {
176        strlcat(wd, "/", MAXPATHLEN);
177        strlcat(wd, name, MAXPATHLEN);
178    }
179    else {
180        strlcpy(wd, name, MAXPATHLEN);
181    }
182    return wd;
183}
184
185
186/**************************************************************************
187 * *at semnatics support functions (like openat, renameat standard funcs)
188 **************************************************************************/
189
190/*
191 * Supports *at semantics if HAVE_ATFUNCS, pass dirfd=-1 to ignore this
192 */
193int copy_file(int dirfd, const char *src, const char *dst, mode_t mode)
194{
195    int    ret = 0;
196    int    sfd = -1;
197    int    dfd = -1;
198    ssize_t cc;
199    size_t  buflen;
200    char   filebuf[8192];
201
202#ifdef HAVE_ATFUNCS
203    if (dirfd == -1)
204        dirfd = AT_FDCWD;
205    sfd = openat(dirfd, src, O_RDONLY);
206#else
207    sfd = open(src, O_RDONLY);
208#endif
209    if (sfd < 0) {
210        LOG(log_error, logtype_afpd, "copy_file('%s'/'%s'): open '%s' error: %s",
211            src, dst, src, strerror(errno));
212        return -1;
213    }
214
215    if ((dfd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, mode)) < 0) {
216        LOG(log_error, logtype_afpd, "copy_file('%s'/'%s'): open '%s' error: %s",
217            src, dst, dst, strerror(errno));
218        ret = -1;
219        goto exit;
220    }
221
222    while ((cc = read(sfd, filebuf, sizeof(filebuf)))) {
223        if (cc < 0) {
224            if (errno == EINTR)
225                continue;
226            LOG(log_error, logtype_afpd, "copy_file('%s'/'%s'): read '%s' error: %s",
227                src, dst, src, strerror(errno));
228            ret = -1;
229            goto exit;
230        }
231
232        buflen = cc;
233        while (buflen > 0) {
234            if ((cc = write(dfd, filebuf, buflen)) < 0) {
235                if (errno == EINTR)
236                    continue;
237                LOG(log_error, logtype_afpd, "copy_file('%s'/'%s'): read '%s' error: %s",
238                    src, dst, dst, strerror(errno));
239                ret = -1;
240                goto exit;
241            }
242            buflen -= cc;
243        }
244    }
245
246exit:
247    if (sfd != -1)
248        close(sfd);
249
250    if (dfd != -1) {
251        int err;
252
253        err = close(dfd);
254        if (!ret && err) {
255            /* don't bother to report an error if there's already one */
256            LOG(log_error, logtype_afpd, "copy_file('%s'/'%s'): close '%s' error: %s",
257                src, dst, dst, strerror(errno));
258            ret = -1;
259        }
260    }
261
262    return ret;
263}
264
265/*
266 * at wrapper for netatalk_unlink
267 */
268int netatalk_unlinkat(int dirfd, const char *name)
269{
270#ifdef HAVE_ATFUNCS
271    if (dirfd == -1)
272        dirfd = AT_FDCWD;
273
274    if (unlinkat(dirfd, name, 0) < 0) {
275        switch (errno) {
276        case ENOENT :
277            break;
278        case EROFS:
279            return AFPERR_VLOCK;
280        case EPERM:
281        case EACCES :
282            return AFPERR_ACCESS;
283        default :
284            return AFPERR_PARAM;
285        }
286    }
287    return AFP_OK;
288#else
289    return netatalk_unlink(name);
290#endif
291
292    /* DEADC0DE */
293    return 0;
294}
295
296/*
297 * @brief This is equivalent of unix rename()
298 *
299 * unix_rename mulitplexes rename and renameat. If we dont HAVE_ATFUNCS, sfd and dfd
300 * are ignored.
301 *
302 * @param sfd        (r) if we HAVE_ATFUNCS, -1 gives AT_FDCWD
303 * @param oldpath    (r) guess what
304 * @param dfd        (r) same as sfd
305 * @param newpath    (r) guess what
306 */
307int unix_rename(int sfd, const char *oldpath, int dfd, const char *newpath)
308{
309#ifdef HAVE_ATFUNCS
310    if (sfd == -1)
311        sfd = AT_FDCWD;
312    if (dfd == -1)
313        dfd = AT_FDCWD;
314
315    if (renameat(sfd, oldpath, dfd, newpath) < 0)
316        return -1;
317#else
318    if (rename(oldpath, newpath) < 0)
319        return -1;
320#endif  /* HAVE_ATFUNCS */
321
322    return 0;
323}
324
325/*
326 * @brief stat/fsstatat multiplexer
327 *
328 * statat mulitplexes stat and fstatat. If we dont HAVE_ATFUNCS, dirfd is ignored.
329 *
330 * @param dirfd   (r) Only used if HAVE_ATFUNCS, ignored else, -1 gives AT_FDCWD
331 * @param path    (r) pathname
332 * @param st      (rw) pointer to struct stat
333 */
334int statat(int dirfd, const char *path, struct stat *st)
335{
336#ifdef HAVE_ATFUNCS
337    if (dirfd == -1)
338        dirfd = AT_FDCWD;
339    return (fstatat(dirfd, path, st, 0));
340#else
341    return (stat(path, st));
342#endif
343
344    /* DEADC0DE */
345    return -1;
346}
347
348/*
349 * @brief lstat/fsstatat multiplexer
350 *
351 * lstatat mulitplexes lstat and fstatat. If we dont HAVE_ATFUNCS, dirfd is ignored.
352 *
353 * @param dirfd   (r) Only used if HAVE_ATFUNCS, ignored else, -1 gives AT_FDCWD
354 * @param path    (r) pathname
355 * @param st      (rw) pointer to struct stat
356 */
357int lstatat(int dirfd, const char *path, struct stat *st)
358{
359#ifdef HAVE_ATFUNCS
360    if (dirfd == -1)
361        dirfd = AT_FDCWD;
362    return (fstatat(dirfd, path, st, AT_SYMLINK_NOFOLLOW));
363#else
364    return (lstat(path, st));
365#endif
366
367    /* DEADC0DE */
368    return -1;
369}
370
371/*
372 * @brief opendir wrapper for *at semantics support
373 *
374 * opendirat chdirs to dirfd if dirfd != -1 before calling opendir on path.
375 *
376 * @param dirfd   (r) if != -1, chdir(dirfd) before opendir(path)
377 * @param path    (r) pathname
378 */
379DIR *opendirat(int dirfd, const char *path)
380{
381    DIR *ret;
382    int cwd = -1;
383
384    if (dirfd != -1) {
385        if (((cwd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) {
386            ret = NULL;
387            goto exit;
388        }
389    }
390
391    ret = opendir(path);
392
393    if (dirfd != -1 && fchdir(cwd) != 0) {
394        LOG(log_error, logtype_afpd, "opendirat: cant chdir back. exit!");
395        exit(EXITERR_SYS);
396    }
397
398exit:
399    if (cwd != -1)
400        close(cwd);
401
402    return ret;
403}
404