]> arthur.barton.de Git - netatalk.git/blob - libatalk/vfs/unix.c
return the right error code when deleting afp current directory if it's already been...
[netatalk.git] / libatalk / vfs / unix.c
1 /*
2  * $Id: unix.c,v 1.8 2010-01-26 08:14:09 didg 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 result = mode;
84     mode_t mask = S_IRWXU | S_IRWXG | S_IRWXO;  /* rwx for owner group and other, by default */
85
86     if (!st) {
87         if (stat(name, &sb) != 0)
88             return -1;
89         st = &sb;
90     }
91
92     result |= st->st_mode & ~mask; /* keep other bits from previous mode */
93
94     LOG(log_debug, logtype_afpd, "setfilmode('%s', mode:%04o, vmask:%04o) {st_mode:%04o, chmod:%04o}",
95         fullpathname(name), mode, v_umask, st->st_mode, result);
96
97     if ( chmod( name,  mode & ~v_umask ) < 0 && errno != EPERM ) {
98         return -1;
99     }
100     return 0;
101 }
102
103 /* -------------------
104    system rmdir with afp error code.
105 */
106 int netatalk_rmdir_all_errors(const char *name)
107 {
108     if (rmdir(name) < 0) {
109         switch ( errno ) {
110         case ENOENT :
111             return AFPERR_NOOBJ;
112         case ENOTEMPTY :
113             return AFPERR_DIRNEMPT;
114         case EPERM:
115         case EACCES :
116             return AFPERR_ACCESS;
117         case EROFS:
118             return AFPERR_VLOCK;
119         default :
120             return AFPERR_PARAM;
121         }
122     }
123     return AFP_OK;
124 }
125
126 /* -------------------
127    system rmdir with afp error code.
128    ENOENT is not an error.
129 */
130 int netatalk_rmdir(const char *name)
131 {
132     int ret = netatalk_rmdir_all_errors(name);
133     if (ret == AFPERR_NOOBJ)
134         return AFP_OK;
135     return ret;
136 }
137
138 /* -------------------
139    system unlink with afp error code.
140    ENOENT is not an error.
141 */
142 int netatalk_unlink(const char *name)
143 {
144     if (unlink(name) < 0) {
145         switch (errno) {
146         case ENOENT :
147             break;
148         case EROFS:
149             return AFPERR_VLOCK;
150         case EPERM:
151         case EACCES :
152             return AFPERR_ACCESS;
153         default :
154             return AFPERR_PARAM;
155         }
156     }
157     return AFP_OK;
158 }
159
160 char *fullpathname(const char *name)
161 {
162     static char wd[ MAXPATHLEN + 1];
163
164     if ( getcwd( wd , MAXPATHLEN) ) {
165         strlcat(wd, "/", MAXPATHLEN);
166         strlcat(wd, name, MAXPATHLEN);
167     }
168     else {
169         strlcpy(wd, name, MAXPATHLEN);
170     }
171     return wd;
172 }
173
174 int copy_file(const char *src, const char *dst, mode_t mode)
175 {
176     int    ret = 0;
177     int    sfd = -1;
178     int    dfd = -1;
179     ssize_t cc;
180     size_t  buflen;
181     char   filebuf[8192];
182
183     if ((sfd = open(src, O_RDONLY)) < 0) {
184         LOG(log_error, logtype_afpd, "copy_file('%s'/'%s'): open '%s' error: %s",
185             src, dst, src, strerror(errno));
186         return -1;
187     }
188
189     if ((dfd = open(dst, O_WRONLY | O_CREAT | O_EXCL, mode)) < 0) {
190         LOG(log_error, logtype_afpd, "copy_file('%s'/'%s'): open '%s' error: %s",
191             src, dst, dst, strerror(errno));
192         ret = -1;
193         goto exit;
194     }
195
196     while ((cc = read(sfd, filebuf, sizeof(filebuf)))) {
197         if (cc < 0) {
198             if (errno == EINTR)
199                 continue;
200             LOG(log_error, logtype_afpd, "copy_file('%s'/'%s'): read '%s' error: %s",
201                 src, dst, src, strerror(errno));
202             ret = -1;
203             goto exit;
204         }
205
206         buflen = cc;
207         while (buflen > 0) {
208             if ((cc = write(dfd, filebuf, buflen)) < 0) {
209                 if (errno == EINTR)
210                     continue;
211                 LOG(log_error, logtype_afpd, "copy_file('%s'/'%s'): read '%s' error: %s",
212                     src, dst, dst, strerror(errno));
213                 ret = -1;
214                 goto exit;
215             }
216             buflen -= cc;
217         }
218     }
219
220 exit:
221     if (sfd != -1)
222         close(sfd);
223
224     if (dfd != -1) {
225         int err;
226
227         err = close(dfd);
228         if (!ret && err) {
229             /* don't bother to report an error if there's already one */
230             LOG(log_error, logtype_afpd, "copy_file('%s'/'%s'): close '%s' error: %s",
231                 src, dst, dst, strerror(errno));
232             ret = -1;
233         }
234     }
235
236     return ret;
237 }
238
239 /* This is equivalent of unix rename(). */
240 int unix_rename(const char *oldpath, const char *newpath)
241 {
242 #if 0
243     char pd_name[PATH_MAX+1];
244     int i;
245     struct stat pd_stat;
246     uid_t uid;
247 #endif
248
249     if (rename(oldpath, newpath) < 0)
250         return -1;
251 #if 0
252     for (i = 0; i <= PATH_MAX && newpath[i] != '\0'; i++)
253         pd_name[i] = newpath[i];
254     pd_name[i] = '\0';
255
256     while (i > 0 && pd_name[i] != '/') i--;
257     if (pd_name[i] == '/') i++;
258
259     pd_name[i++] = '.'; pd_name[i++] = '\0';
260
261     if (stat(pd_name, &pd_stat) < 0) {
262         LOG(log_error, logtype_afpd, "stat() of parent dir failed: pd_name = %s, uid = %d: %s",
263             pd_name, geteuid(), strerror(errno));
264         return 0;
265     }
266
267     /* So we have SGID bit set... */
268     if ((S_ISGID & pd_stat.st_mode) != 0) {
269         uid = geteuid();
270         if (seteuid(0) < 0)
271             LOG(log_error, logtype_afpd, "seteuid() failed: %s", strerror(errno));
272         if (recursive_chown(newpath, uid, pd_stat.st_gid) < 0)
273             LOG(log_error, logtype_afpd, "chown() of parent dir failed: newpath=%s, uid=%d: %s",
274                 pd_name, geteuid(), strerror(errno));
275         seteuid(uid);
276     }
277 #endif
278     return 0;
279 }