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