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