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