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