]> arthur.barton.de Git - netatalk.git/blob - libatalk/vfs/unix.c
Remove noadouble option
[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 #include <atalk/errchk.h>
27 #include <atalk/ea.h>
28
29 /* -----------------------------
30    a dropbox is a folder where w is set but not r eg:
31    rwx-wx-wx or rwx-wx--
32    rwx----wx (is not asked by a Mac with OS >= 8.0 ?)
33 */
34 int stickydirmode(const char *name, const mode_t mode, const int dropbox, const mode_t v_umask)
35 {
36     int retval = 0;
37
38     /*
39      *  Ignore EPERM errors:  We may be dealing with a directory that is
40      *  group writable, in which case chmod will fail.
41      */
42     if ((chmod_acl( name, (DIRBITS | mode) & ~v_umask ) < 0) && errno != EPERM) {
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 /* Copy all file data from one file fd to another */
154 int copy_file_fd(int sfd, int dfd)
155 {
156     EC_INIT;
157     ssize_t cc;
158     size_t  buflen;
159     char   filebuf[NETATALK_DIOSZ_STACK];
160
161     while ((cc = read(sfd, filebuf, sizeof(filebuf)))) {
162         if (cc < 0) {
163             if (errno == EINTR)
164                 continue;
165             LOG(log_error, logtype_afpd, "copy_file_fd: %s", strerror(errno));
166             EC_FAIL;
167         }
168
169         buflen = cc;
170         while (buflen > 0) {
171             if ((cc = write(dfd, filebuf, buflen)) < 0) {
172                 if (errno == EINTR)
173                     continue;
174                 LOG(log_error, logtype_afpd, "copy_file_fd: %s", strerror(errno));
175                 EC_FAIL;
176             }
177             buflen -= cc;
178         }
179     }
180
181 EC_CLEANUP:
182     EC_EXIT;
183 }
184
185 /* 
186  * Supports *at semantics if HAVE_ATFUNCS, pass dirfd=-1 to ignore this
187  */
188 int copy_file(int dirfd, const char *src, const char *dst, mode_t mode)
189 {
190     int    ret = 0;
191     int    sfd = -1;
192     int    dfd = -1;
193     ssize_t cc;
194     size_t  buflen;
195     char   filebuf[NETATALK_DIOSZ_STACK];
196
197 #ifdef HAVE_ATFUNCS
198     if (dirfd == -1)
199         dirfd = AT_FDCWD;
200     sfd = openat(dirfd, src, O_RDONLY);
201 #else
202     sfd = open(src, O_RDONLY);
203 #endif
204     if (sfd < 0) {
205         LOG(log_info, logtype_afpd, "copy_file('%s'/'%s'): open '%s' error: %s",
206             src, dst, src, strerror(errno));
207         return -1;
208     }
209
210     if ((dfd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, mode)) < 0) {
211         LOG(log_info, logtype_afpd, "copy_file('%s'/'%s'): open '%s' error: %s",
212             src, dst, dst, strerror(errno));
213         ret = -1;
214         goto exit;
215     }
216
217     ret = copy_file_fd(sfd, dfd);
218
219 exit:
220     if (sfd != -1)
221         close(sfd);
222
223     if (dfd != -1) {
224         int err;
225
226         err = close(dfd);
227         if (!ret && err) {
228             /* don't bother to report an error if there's already one */
229             LOG(log_error, logtype_afpd, "copy_file('%s'/'%s'): close '%s' error: %s",
230                 src, dst, dst, strerror(errno));
231             ret = -1;
232         }
233     }
234
235     return ret;
236 }
237
238 /*!
239  * Copy an EA from one file to another
240  *
241  * Supports *at semantics if HAVE_ATFUNCS, pass dirfd=-1 to ignore this
242  */
243 int copy_ea(const char *ea, int dirfd, const char *src, const char *dst, mode_t mode)
244 {
245     EC_INIT;
246     int    sfd = -1;
247     int    dfd = -1;
248     size_t easize;
249     char   *eabuf = NULL;
250
251 #ifdef HAVE_ATFUNCS
252     if (dirfd == -1)
253         dirfd = AT_FDCWD;
254     EC_NEG1_LOG( sfd = openat(dirfd, src, O_RDONLY) );
255 #else
256     EC_NEG1_LOG( sfd = open(src, O_RDONLY) );
257 #endif
258     EC_NEG1_LOG( dfd = open(dst, O_WRONLY, mode) );
259
260     if ((easize = sys_fgetxattr(sfd, ea, NULL, 0)) > 0) {
261         EC_NULL_LOG( eabuf = malloc(easize));
262         EC_NEG1_LOG( easize = sys_fgetxattr(sfd, ea, eabuf, easize) );
263         EC_NEG1_LOG( easize = sys_fsetxattr(dfd, ea, eabuf, easize, 0) );
264     }
265
266 EC_CLEANUP:
267     if (sfd != -1)
268         close(sfd);
269     if (dfd != -1)
270         close(dfd);
271     free(eabuf);
272     EC_EXIT;
273 }
274
275 /* 
276  * at wrapper for netatalk_unlink
277  */
278 int netatalk_unlinkat(int dirfd, const char *name)
279 {
280 #ifdef HAVE_ATFUNCS
281     if (dirfd == -1)
282         dirfd = AT_FDCWD;
283
284     if (unlinkat(dirfd, name, 0) < 0) {
285         switch (errno) {
286         case ENOENT :
287             break;
288         case EROFS:
289             return AFPERR_VLOCK;
290         case EPERM:
291         case EACCES :
292             return AFPERR_ACCESS;
293         default :
294             return AFPERR_PARAM;
295         }
296     }
297     return AFP_OK;
298 #else
299     return netatalk_unlink(name);
300 #endif
301
302     /* DEADC0DE */
303     return 0;
304 }
305
306 /*
307  * @brief This is equivalent of unix rename()
308  *
309  * unix_rename mulitplexes rename and renameat. If we dont HAVE_ATFUNCS, sfd and dfd
310  * are ignored.
311  *
312  * @param sfd        (r) if we HAVE_ATFUNCS, -1 gives AT_FDCWD
313  * @param oldpath    (r) guess what
314  * @param dfd        (r) same as sfd
315  * @param newpath    (r) guess what
316  */
317 int unix_rename(int sfd, const char *oldpath, int dfd, const char *newpath)
318 {
319 #ifdef HAVE_ATFUNCS
320     if (sfd == -1)
321         sfd = AT_FDCWD;
322     if (dfd == -1)
323         dfd = AT_FDCWD;
324
325     if (renameat(sfd, oldpath, dfd, newpath) < 0)
326         return -1;        
327 #else
328     if (rename(oldpath, newpath) < 0)
329         return -1;
330 #endif  /* HAVE_ATFUNCS */
331
332     return 0;
333 }
334
335 /* 
336  * @brief stat/fsstatat multiplexer
337  *
338  * statat mulitplexes stat and fstatat. If we dont HAVE_ATFUNCS, dirfd is ignored.
339  *
340  * @param dirfd   (r) Only used if HAVE_ATFUNCS, ignored else, -1 gives AT_FDCWD
341  * @param path    (r) pathname
342  * @param st      (rw) pointer to struct stat
343  */
344 int statat(int dirfd, const char *path, struct stat *st)
345 {
346 #ifdef HAVE_ATFUNCS
347     if (dirfd == -1)
348         dirfd = AT_FDCWD;
349     return (fstatat(dirfd, path, st, 0));
350 #else
351     return (stat(path, st));
352 #endif            
353
354     /* DEADC0DE */
355     return -1;
356 }
357
358 /* 
359  * @brief lstat/fsstatat multiplexer
360  *
361  * lstatat mulitplexes lstat and fstatat. If we dont HAVE_ATFUNCS, dirfd is ignored.
362  *
363  * @param dirfd   (r) Only used if HAVE_ATFUNCS, ignored else, -1 gives AT_FDCWD
364  * @param path    (r) pathname
365  * @param st      (rw) pointer to struct stat
366  */
367 int lstatat(int dirfd, const char *path, struct stat *st)
368 {
369 #ifdef HAVE_ATFUNCS
370     if (dirfd == -1)
371         dirfd = AT_FDCWD;
372     return (fstatat(dirfd, path, st, AT_SYMLINK_NOFOLLOW));
373 #else
374     return (lstat(path, st));
375 #endif            
376
377     /* DEADC0DE */
378     return -1;
379 }
380
381 /* 
382  * @brief opendir wrapper for *at semantics support
383  *
384  * opendirat chdirs to dirfd if dirfd != -1 before calling opendir on path.
385  *
386  * @param dirfd   (r) if != -1, chdir(dirfd) before opendir(path)
387  * @param path    (r) pathname
388  */
389 DIR *opendirat(int dirfd, const char *path)
390 {
391     DIR *ret;
392     int cwd = -1;
393
394     if (dirfd != -1) {
395         if (((cwd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) {
396             ret = NULL;
397             goto exit;
398         }
399     }
400
401     ret = opendir(path);
402
403     if (dirfd != -1 && fchdir(cwd) != 0) {
404         LOG(log_error, logtype_afpd, "opendirat: cant chdir back. exit!");
405         exit(EXITERR_SYS);
406     }
407
408 exit:
409     if (cwd != -1)
410         close(cwd);
411
412     return ret;
413 }