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