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