1 /* vi:ai:et:ts=8 sw=2
2  */
3 /*
4  * wzdftpd - a modular and cool ftp server
5  * Copyright (C) 2002-2004  Pierre Chifflier
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20  *
21  * As a special exemption, Pierre Chifflier
22  * and other respective copyright holders give permission to link this program
23  * with OpenSSL, and distribute the resulting executable, without including
24  * the source code for OpenSSL in the source distribution.
25  */
26 
27 #include "wzd_all.h"
28 
29 #ifndef WZD_USE_PCH
30 
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <errno.h>
37 
38 #endif /* WZD_USE_PCH */
39 
40 /** \file wzd_file.c
41  * \brief Files and directories functions
42  *
43  * Permissions are stored in a file present in each directory on the server.
44  * This allows portable function, and features like symbolic links on
45  * systems which does not have links (like windows).
46  *
47  * \addtogroup libwzd_core
48  * @{
49  */
50 
51 #ifdef WIN32
52 #include <winsock2.h>
53 #include <io.h>
54 #include <direct.h> /* _mkdir */
55 #include <sys/locking.h> /* _locking */
56 #define _WIN32_WINNT  0x500
57 #include <windows.h>
58 #include <tchar.h>
59 #include <winioctl.h>
60 
61 // Since MS apparently removed this struct (and its documentation) from
62 // the W2k SDK, but still refer to it in 'winioctl.h' for the specific
63 // IOCTLs, I decided to rename it and make it available.
64 // I've made some modifications to this one for easier access.
65 //
66 // Structure for FSCTL_SET_REPARSE_POINT, FSCTL_GET_REPARSE_POINT, and
67 // FSCTL_DELETE_REPARSE_POINT.
68 // This version of the reparse data buffer is only for Microsoft tags.
69 
70 typedef struct
71 {
72     DWORD  ReparseTag;
73     WORD   ReparseDataLength;
74     WORD   Reserved;
75 
76     // IO_REPARSE_TAG_MOUNT_POINT specifics follow
77     WORD   SubstituteNameOffset;
78     WORD   SubstituteNameLength;
79     WORD   PrintNameOffset;
80     WORD   PrintNameLength;
81     WCHAR  PathBuffer[1];
82 
83     // Some helper functions
84 //	bool Init(LPCSTR szJunctionPoint);
85 //	bool Init(LPCWSTR wszJunctionPoint);
86 //	int BytesForIoControl() const;
87 } TMN_REPARSE_DATA_BUFFER;
88 
89 #define TMN_REPARSE_DATA_BUFFER_HEADER_SIZE \
90 			FIELD_OFFSET(TMN_REPARSE_DATA_BUFFER, SubstituteNameOffset)
91 
92 
93 // These have the wrong values in pre-W2k SDKs, why I redefine them here.
94 #if !defined(FSCTL_SET_REPARSE_POINT) || \
95 	(FSCTL_SET_REPARSE_POINT != 0x900a4)
96 #undef FSCTL_SET_REPARSE_POINT
97 #define FSCTL_SET_REPARSE_POINT  CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 41, METHOD_BUFFERED, FILE_ANY_ACCESS)
98 #endif
99 
100 #if !defined(FSCTL_DELETE_REPARSE_POINT) || \
101 	(FSCTL_DELETE_REPARSE_POINT != 0x900ac)
102 #undef FSCTL_DELETE_REPARSE_POINT
103 #define FSCTL_DELETE_REPARSE_POINT      CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 43, METHOD_BUFFERED, FILE_ANY_ACCESS)
104 #endif
105 
106 
107 
108 
109 #else
110 #include <unistd.h>
111 
112 #include <sys/types.h>
113 #include <sys/socket.h>
114 #include <netinet/in.h>
115 #include <arpa/inet.h>
116 
117 #include <dirent.h>
118 #endif
119 
120 #ifndef HAVE_STRTOK_R
121 # include "libwzd-base/wzd_strtok_r.h"
122 #endif
123 
124 #include <fcntl.h> /* O_RDONLY */
125 
126 #include "wzd_structs.h"
127 
128 #include "wzd_libmain.h"
129 #include "wzd_log.h"
130 #include "wzd_misc.h"
131 #include "wzd_file.h"
132 #include "wzd_fs.h"
133 #include "wzd_group.h"
134 #include "wzd_cache.h"
135 #include "wzd_perm.h"
136 #include "wzd_user.h"
137 #include "wzd_vfs.h"
138 
139 
140 
141 /*#define _HAS_MMAP*/
142 
143 #ifdef _HAS_MMAP
144 #include <sys/mman.h>
145 #endif
146 
147 
148 #include "wzd_debug.h"
149 
150 
151 #define BUFFER_LEN	4096
152 
153 /************ PRIVATE FUNCTIONS **************/
154 
155 /** \brief Get default permission for user
156  * \param[in] wanted_right action to be evaluated
157  * \param[in] user user definition
158  *
159  * \todo XXX this function is badly named, userperms is no more a default action, but more a permissions mask
160  *
161  * Default permissions are set by the userperms field of the user.
162  * \return 0 if user is allowed to perform action
163  */
_default_perm(unsigned long wanted_right,wzd_user_t * user)164 static int _default_perm(unsigned long wanted_right, wzd_user_t * user)
165 {
166   return (( wanted_right & user->userperms ) == 0);
167 }
168 
169 /** Free file list recursively
170  * \note locks SET_MUTEX_FILE_T
171  */
free_file_recursive(struct wzd_file_t * file)172 void free_file_recursive(struct wzd_file_t * file)
173 {
174   struct wzd_file_t * next_file;
175   wzd_acl_line_t *acl_current,*acl_next;
176 
177   if (!file) return;
178   WZD_MUTEX_LOCK(SET_MUTEX_FILE_T);
179   do {
180     next_file = file->next_file;
181     acl_current = file->acl;
182     if (acl_current) {
183       do {
184         acl_next = acl_current->next_acl;
185         wzd_free(acl_current);
186         acl_current = acl_next;
187       } while (acl_current);
188     }
189     if (file->data) free(file->data);
190     wzd_free (file);
191     file = next_file;
192   } while (file);
193   WZD_MUTEX_UNLOCK(SET_MUTEX_FILE_T);
194 }
195 
196 /** \brief Find file \a name in file list
197  * \param[in] name file name
198  * \param[in] first file list head
199  * \return
200  *  - file if found
201  *  - NULL if not found
202  */
find_file(const char * name,struct wzd_file_t * first)203 static struct wzd_file_t * find_file(const char *name, struct wzd_file_t *first)
204 {
205   struct wzd_file_t *current=first;
206 
207   WZD_MUTEX_LOCK(SET_MUTEX_FILE_T);
208   while (current) {
209     if (strcmp(name,current->filename)==0) {
210       WZD_MUTEX_UNLOCK(SET_MUTEX_FILE_T);
211       return current;
212     }
213     current = current->next_file;
214   }
215   WZD_MUTEX_UNLOCK(SET_MUTEX_FILE_T);
216   return NULL;
217 }
218 
219 /** \brief Remove file from linked list
220  * \param[in] name file name
221  * \param[in,out] file list head
222  * \return
223  *  - the removed item if found (which must be freed using free_file_recursive()
224  *  - NULL if not found
225  */
remove_file(const char * name,struct wzd_file_t ** first)226 static struct wzd_file_t * remove_file(const char *name, struct wzd_file_t **first)
227 {
228   struct wzd_file_t *current=*first,*prev,*removed;
229 
230   if (!current) return NULL;
231 
232   WZD_MUTEX_LOCK(SET_MUTEX_FILE_T);
233   /* first to be removed ? */
234   if (strcmp(name,current->filename)==0) {
235     removed = current;
236     *first = removed->next_file;
237     removed->next_file = NULL;
238     WZD_MUTEX_UNLOCK(SET_MUTEX_FILE_T);
239     return removed;
240   }
241 
242   prev = current;
243   current = current->next_file;
244 
245   while (current) {
246     if (strcmp(name,current->filename)==0) {
247       removed = current;
248       prev->next_file = current->next_file;
249       current->next_file = NULL;
250       WZD_MUTEX_UNLOCK(SET_MUTEX_FILE_T);
251       return removed;
252     }
253     prev = current;
254     current = current->next_file;
255   } /* while current */
256   WZD_MUTEX_UNLOCK(SET_MUTEX_FILE_T);
257   return NULL;
258 }
259 
260 /** \brief Insert a new file structure in list, sorted by name
261  * \param[in] entry file definition
262  * \param[in,out] tab pointer to file list
263  */
file_insert_sorted(struct wzd_file_t * entry,struct wzd_file_t ** tab)264 void file_insert_sorted(struct wzd_file_t *entry, struct wzd_file_t **tab)
265 {
266   struct wzd_file_t *it  = *tab;
267   struct wzd_file_t *itp = NULL;
268 
269   if ( ! *tab ) {
270     *tab = entry;
271     return;
272   }
273 
274   WZD_MUTEX_LOCK(SET_MUTEX_FILE_T);
275   while (it) {
276     if (strcmp(entry->filename,it->filename)>0)
277     {
278       itp = it;
279       it = it->next_file;
280       continue;
281     }
282 
283     /* we insert here */
284 
285     /* head insertion */
286     if (itp == NULL) {
287       entry->next_file = *tab;
288       *tab = entry;
289       WZD_MUTEX_UNLOCK(SET_MUTEX_FILE_T);
290       return;
291     }
292 
293     /* middle-insertion */
294     entry->next_file = it;
295     itp->next_file = entry;
296 
297     WZD_MUTEX_UNLOCK(SET_MUTEX_FILE_T);
298     return;
299   }
300 
301   /* tail insertion */
302   /* itp can't be NULL here, the first case would have trapped it */
303   itp->next_file = entry;
304 
305   WZD_MUTEX_UNLOCK(SET_MUTEX_FILE_T);
306   return;
307 }
308 
309 /** \brief Return ACL corresonding to \a username for \a file
310  * \param[in] username User name
311  * \param[in] file file structure
312  * \return The ACL structure, or NULL
313  */
find_acl(const char * username,struct wzd_file_t * file)314 static wzd_acl_line_t * find_acl(const char * username, struct wzd_file_t * file)
315 {
316   wzd_acl_line_t *current = file->acl;
317 
318   WZD_MUTEX_LOCK(SET_MUTEX_ACL_T);
319   while (current) {
320     if (strcmp(username,current->user)==0) {
321       WZD_MUTEX_UNLOCK(SET_MUTEX_ACL_T);
322       return current;
323     }
324     current = current->next_acl;
325   }
326   WZD_MUTEX_UNLOCK(SET_MUTEX_ACL_T);
327   return NULL;
328 }
329 
330 /** \brief Create a structure for file \a name, set owner and group, and append it to list \a first
331  * \param[in] name file name
332  * \param[in] owner file owner
333  * \param[in] group file group
334  * \param[in,out] first file list
335  * \return a pointer to the new file structure
336  */
add_new_file(const char * name,const char * owner,const char * group,struct wzd_file_t ** first)337 static struct wzd_file_t * add_new_file(const char *name, const char *owner, const char *group, struct wzd_file_t **first)
338 {
339   struct wzd_file_t *current, *new_file;
340 
341   WZD_MUTEX_LOCK(SET_MUTEX_FILE_T);
342   new_file = wzd_malloc(sizeof(struct wzd_file_t));
343   strncpy(new_file->filename,name,256);
344   memset(new_file->owner,0,256);
345   if (owner) strncpy(new_file->owner,owner,256);
346   memset(new_file->group,0,256);
347   if (group) strncpy(new_file->group,group,256);
348   new_file->acl = NULL;
349   new_file->permissions = mainConfig->umask; /* TODO XXX FIXME hardcoded */
350   new_file->kind = FILE_NOTSET;
351   new_file->data = NULL;
352   new_file->next_file = NULL;
353   if (*first == NULL) {
354     *first = new_file;
355   } else {
356     current = *first;
357     while (current->next_file)
358       current = current->next_file;
359     current->next_file = new_file;
360   }
361   WZD_MUTEX_UNLOCK(SET_MUTEX_FILE_T);
362   return new_file;
363 }
364 
365 /** Copy file structure and members
366  * \param[in] file_cur file structure
367  * \return a newly allocated file structure copied from \a file_cur, or NULL
368  * \note one field is changed: next_file is set to NULL to avoid side effects.
369  */
file_deep_copy(struct wzd_file_t * file_cur)370 struct wzd_file_t * file_deep_copy(struct wzd_file_t *file_cur)
371 {
372   struct wzd_file_t * new_file=NULL;
373   wzd_acl_line_t * acl_current, * acl_new, *acl_next;
374 
375   if (!file_cur) return NULL;
376 
377   WZD_MUTEX_LOCK(SET_MUTEX_FILE_T);
378   new_file = wzd_malloc(sizeof(struct wzd_file_t));
379   memcpy(new_file, file_cur, sizeof(struct wzd_file_t));
380   if (file_cur->data)
381     new_file->data = strdup( (char*)file_cur->data ); /** \todo we do not know size */
382 
383   if (file_cur->acl) {
384     acl_new = malloc(sizeof(wzd_acl_line_t));
385     memcpy(acl_new, file_cur->acl, sizeof(wzd_acl_line_t));
386     acl_new->next_acl = NULL;
387     new_file->acl = acl_new;
388     acl_current = file_cur->acl->next_acl;
389     while (acl_current) {
390       acl_next = malloc(sizeof(wzd_acl_line_t));
391       memcpy(acl_next, file_cur->acl, sizeof(wzd_acl_line_t));
392       acl_next->next_acl = NULL;
393       acl_new->next_acl = acl_next;
394       acl_new = acl_next;
395       acl_current = acl_current->next_acl;
396     }
397   }
398 
399   /* exception: we set next_file to NULL to avoid side effects */
400   new_file->next_file = NULL;
401 
402   WZD_MUTEX_UNLOCK(SET_MUTEX_FILE_T);
403   return new_file;
404 }
405 
406 /** \brief Add new ACL for a file,user
407  * \param[in] filename file name
408  * \param[in] user
409  * \param[in] rights permission line
410  * \param[in,out] file file structure
411  * \todo return value on error
412  * \todo rename function to use common name standards
413  */
addAcl(const char * filename,const char * user,const char * rights,struct wzd_file_t * file)414 static void addAcl(const char *filename, const char *user, const char *rights, struct wzd_file_t * file)
415 {
416   wzd_acl_line_t * acl_current, * acl_new;
417 
418   WZD_MUTEX_LOCK(SET_MUTEX_ACL_T);
419 
420   acl_new = wzd_malloc(sizeof(wzd_acl_line_t));
421   strncpy(acl_new->user,user,256);
422   strncpy(acl_new->perms,rights,3);
423 
424   /* head insertion */
425   acl_current = file->acl;
426   if (!acl_current) { /* simple case, first insertion */
427     file->acl = acl_new;
428     acl_new->next_acl = NULL;
429     WZD_MUTEX_UNLOCK(SET_MUTEX_ACL_T);
430     return;
431   }
432 
433   while (acl_current) {
434     if (strcmp(acl_current->user,user)==0) { /* found ! */
435       strncpy(acl_current->perms,rights,3); /* replace old perms */
436       wzd_free (acl_new);
437       WZD_MUTEX_UNLOCK(SET_MUTEX_ACL_T);
438       return;
439     }
440     acl_current = acl_current->next_acl;
441   }
442 
443   /* new acl for this file */
444   acl_new->next_acl = file->acl;
445   file->acl = acl_new;
446   WZD_MUTEX_UNLOCK(SET_MUTEX_ACL_T);
447 }
448 
449 /** Read permission file and decode it
450  * \param[in] permfile full path to permission file
451  * \param[out] pTabFiles address of linked list (which will be allocated) containing file permissions
452  * \return 0 if ok
453  * \todo should be "atomic"
454  */
readPermFile(const char * permfile,struct wzd_file_t ** pTabFiles)455 int readPermFile(const char *permfile, struct wzd_file_t **pTabFiles)
456 {
457   wzd_cache_t * fp;
458   char line_buffer[BUFFER_LEN];
459   struct wzd_file_t *current_file, *ptr_file;
460   char * token1, *token2, *token3, *token4, *token5, *token6;
461   char *ptr;
462 
463   if ( !pTabFiles ) return E_PARAM_NULL;
464 
465   current_file = *pTabFiles;
466 
467   WZD_MUTEX_LOCK(SET_MUTEX_DIRINFO);
468   fp = wzd_cache_open(permfile,O_RDONLY,0644);
469   if (!fp) {
470     wzd_cache_close(fp);
471     WZD_MUTEX_UNLOCK(SET_MUTEX_DIRINFO);
472     return E_FILE_NOEXIST;
473   }
474 
475   ptr = (char*)current_file;
476   current_file = NULL;
477   while ( wzd_cache_gets(fp,line_buffer,BUFFER_LEN-1) )
478   {
479     token1 = strtok_r(line_buffer," \t\r\n",&ptr);
480     if (!token1) continue; /* malformed line */
481     token2 = read_token(NULL, &ptr); /* we can have spaces here */
482     if (!token2) continue; /* malformed line */
483     token3 = read_token(NULL, &ptr); /* we can have spaces here */
484     if (!token3) continue; /* malformed line */
485     token4 = strtok_r(NULL," \t\r\n",&ptr);
486     if (!token4) continue; /* malformed line */
487     /* find file in  list */
488     ptr_file = find_file(token2,*pTabFiles);
489     if (!ptr_file) {
490       ptr_file = add_new_file(token2,0,0,pTabFiles);
491     }
492     if (strcmp(token1,"owner")==0) {
493       token5 = strtok_r(NULL," \t\r\n",&ptr);
494 /*      if (!token5) continue;*/ /* malformed line */
495       strncpy(ptr_file->owner,token3,256);
496       strncpy(ptr_file->group,token4,256);
497       if (token5) {
498         unsigned long ul;
499         ul = strtoul(token5,&ptr,8);
500         if (ptr==token5) continue;
501         ptr_file->permissions = ul;
502       } else { /* default user/group permission */
503         ptr_file->permissions = mainConfig->umask; /** \todo FIXME hardcoded */
504       }
505     }
506     else if (strcmp(token1,"perm")==0) {
507       addAcl(token2,token3,token4,ptr_file);
508     }
509     else if (strcmp(token1,"link")==0) {
510       /** \todo FIXME handle links: set type to link, set destination, set owner/perms */
511       token5 = strtok_r(NULL," \t\r\n",&ptr);
512       if (!token5) continue; /* malformed line */
513       token6 = strtok_r(NULL," \t\r\n",&ptr);
514 
515       ptr_file->kind = FILE_LNK;
516       ptr_file->data = wzd_strdup(token3);
517       strncpy(ptr_file->owner,token4,256);
518       strncpy(ptr_file->group,token5,256);
519       if (token6) {
520         unsigned long ul;
521         ul = strtoul(token6,&ptr,8);
522         if (ptr==token6) continue;
523         ptr_file->permissions = ul;
524       } else { /* default user/group permission */
525         ptr_file->permissions = mainConfig->umask; /** \todo FIXME hardcoded */
526       }
527     }
528   }
529 
530   wzd_cache_close(fp);
531   WZD_MUTEX_UNLOCK(SET_MUTEX_DIRINFO);
532 
533   return E_OK;
534 }
535 
536 /** \brief Write permission file
537  * \param[in] permfile permission file full path
538  * \param[in] pTabFiles address of linked list of permissions
539  * \return 0 if ok
540  */
writePermFile(const char * permfile,struct wzd_file_t ** pTabFiles)541 int writePermFile(const char *permfile, struct wzd_file_t **pTabFiles)
542 {
543   char buffer[BUFFER_LEN];
544   FILE *fp;
545   struct wzd_file_t * file_cur;
546   wzd_acl_line_t * acl_cur;
547   short has_spaces;
548 
549   file_cur = *pTabFiles;
550 
551   if ( !file_cur ) {
552     /* delete permission file */
553     return unlink(permfile);
554   }
555 
556   WZD_MUTEX_LOCK(SET_MUTEX_DIRINFO);
557 
558   fp = fopen(permfile,"w"); /* overwrite any existing file */
559   if (!fp) {
560     WZD_MUTEX_UNLOCK(SET_MUTEX_DIRINFO);
561     return -1;
562   }
563 
564   /* if file_cur->filename contains spaces, we MUST quote it when writing name */
565   while (file_cur) {
566     if (file_cur->kind == FILE_LNK) {
567       if (strchr( (char*)file_cur->data, ' ')) {
568         snprintf(buffer,sizeof(buffer),"link\t%s\t'%s'\t%s\t%s\t%lo\n",
569             file_cur->filename,(char*)file_cur->data,file_cur->owner,file_cur->group,file_cur->permissions);
570       } else {
571         snprintf(buffer,sizeof(buffer),"link\t%s\t%s\t%s\t%s\t%lo\n",
572             file_cur->filename,(char*)file_cur->data,file_cur->owner,file_cur->group,file_cur->permissions);
573       }
574       (void)fwrite(buffer,strlen(buffer),1,fp);
575     } else { /* not a link */
576       has_spaces = (strchr( (char*)file_cur->filename, ' ') != NULL);
577       /* first write owner if available */
578       if (strlen(file_cur->owner)>0 || strlen(file_cur->group)>0) {
579         if (has_spaces)
580           snprintf(buffer,sizeof(buffer),"owner\t'%s'\t%s\t%s\t%lo\n",
581               file_cur->filename,file_cur->owner,file_cur->group,file_cur->permissions);
582         else
583           snprintf(buffer,sizeof(buffer),"owner\t%s\t%s\t%s\t%lo\n",
584               file_cur->filename,file_cur->owner,file_cur->group,file_cur->permissions);
585         (void)fwrite(buffer,strlen(buffer),1,fp);
586       }
587       acl_cur = file_cur->acl;
588       while (acl_cur) {
589         if (has_spaces)
590           snprintf(buffer,sizeof(buffer),"perm\t'%s'\t%s\t%c%c%c\n",
591               file_cur->filename,acl_cur->user,acl_cur->perms[0],acl_cur->perms[1],acl_cur->perms[2]);
592         else
593           snprintf(buffer,sizeof(buffer),"perm\t%s\t%s\t%c%c%c\n",
594               file_cur->filename,acl_cur->user,acl_cur->perms[0],acl_cur->perms[1],acl_cur->perms[2]);
595         (void)fwrite(buffer,strlen(buffer),1,fp);
596         acl_cur = acl_cur->next_acl;
597       }
598     } /* not a link */
599     file_cur = file_cur->next_file;
600   } /* ! while */
601 
602   fclose(fp);
603 
604   /* force cache update */
605   wzd_cache_update(permfile);
606 
607   WZD_MUTEX_UNLOCK(SET_MUTEX_DIRINFO);
608 
609   return 0;
610 }
611 
612 /** Check if user has a specific permission, given a file and the directory containing it
613  * \param[in] dir directory where \a file is stored. it MUST be / terminated
614  * \param[in] wanted_file file name
615  * \param[in] wanted_right permission to evaluate
616  * \param[in] user
617  * \return
618  *  - 0 if user is authorized to perform action
619  *  - 1 if user is not authorized
620  *  - -1 on error
621  */
_checkFileForPerm(const char * dir,const char * wanted_file,unsigned long wanted_right,wzd_user_t * user)622 int _checkFileForPerm(const char *dir, const char * wanted_file, unsigned long wanted_right, wzd_user_t * user)
623 {
624   char perm_filename[WZD_MAX_PATH+1];
625   size_t length, neededlength;
626   struct wzd_file_t * file_list=NULL, * file_cur;
627   wzd_acl_line_t * acl_cur;
628   int ret;
629   int is_dir;
630 
631   /* find the dir containing the perms file */
632   strncpy(perm_filename,dir,WZD_MAX_PATH);
633   neededlength = strlen(HARD_PERMFILE);
634   length = strlen(perm_filename);
635   /* check if !overflow */
636   if ( length+neededlength >= WZD_MAX_PATH )
637       return -1;
638 
639   /* siteop always hav all permissions */
640   if (user->flags && strchr(user->flags,FLAG_SITEOP))
641     return 0;
642 
643   strncpy(perm_filename+length,HARD_PERMFILE,neededlength);
644 
645 /*
646 out_err(LEVEL_HIGH,"%s:%d\n",__FILE__,__LINE__);
647 out_err(LEVEL_HIGH,"dir %s filename %s wanted file %s\n",dir,perm_filename,wanted_file);
648 */
649 
650   ret = readPermFile(perm_filename,&file_list);
651   if (ret) { /* no permissions file */
652     return _default_perm(wanted_right,user);
653   }
654 
655   file_cur = find_file(wanted_file,file_list);
656 
657   if (file_cur) { /* wanted_file is in list */
658     /* now find corresponding acl */
659     acl_cur = find_acl(user->username,file_cur);
660 
661     is_dir = ( strcmp(wanted_file,".")==0 );
662 
663     if (!acl_cur) { /* ! in acl list */
664       /* TODO check if user is owner or group of file, and use perms */
665       {
666         unsigned int i;
667         wzd_group_t * group;
668 /*	out_err(LEVEL_HIGH,"owner %s\n",file_cur->owner);*/
669 /*	out_err(LEVEL_HIGH,"group %s\n",file_cur->group);*/
670 /*	out_err(LEVEL_HIGH,"group %lo\n",file_cur->permissions);*/
671         if (strcmp(user->username,file_cur->owner)==0) {
672           /* NOTE all results are inverted (!=) because we return 0 on success ! */
673           switch (wanted_right) {
674             case RIGHT_LIST:
675             case RIGHT_RETR:
676               ret = (file_cur->permissions & 0400);
677               break;
678             case RIGHT_STOR:
679             case RIGHT_MKDIR:
680             case RIGHT_RMDIR:
681             case RIGHT_RNFR:
682               ret = (file_cur->permissions & 0200);
683               break;
684             case RIGHT_CWD:
685               ret = (file_cur->permissions & 0100);
686               break;
687             default:
688               ret = 0;
689           }
690 /*	  out_err(LEVEL_HIGH,"user is file owner : %d !\n",ret);*/
691           free_file_recursive(file_list);
692           file_list = NULL;
693           return !ret;
694         }
695         for (i=0; i<user->group_num; i++) {
696           group = GetGroupByID(user->groups[i]);
697           if (group && strcmp(group->groupname,file_cur->group)==0) {
698             /* NOTE all results are inverted (!=) because we return 0 on success ! */
699             switch (wanted_right) {
700               case RIGHT_LIST:
701               case RIGHT_RETR:
702                 ret = (file_cur->permissions & 0040);
703                 break;
704               case RIGHT_STOR:
705               case RIGHT_MKDIR:
706               case RIGHT_RMDIR:
707               case RIGHT_RNFR:
708                 ret = (file_cur->permissions & 0020);
709                 break;
710               case RIGHT_CWD:
711                 ret = (file_cur->permissions & 0010);
712                 break;
713               default:
714                 ret = 0;
715             }
716             /*	    out_err(LEVEL_HIGH,"user is in group : %d !\n",ret);*/
717             free_file_recursive(file_list);
718             file_list = NULL;
719             return !ret;
720           }
721         }
722       }
723 
724       /* NOTE all results are inverted (!=) because we return 0 on success ! */
725       switch (wanted_right) {
726         case RIGHT_LIST:
727         case RIGHT_RETR:
728           ret = (file_cur->permissions & 0004);
729           break;
730         case RIGHT_STOR:
731         case RIGHT_MKDIR:
732         case RIGHT_RMDIR:
733         case RIGHT_RNFR:
734           ret = (file_cur->permissions & 0002);
735           break;
736         case RIGHT_CWD:
737           ret = (file_cur->permissions & 0001);
738           break;
739         default:
740           ret = 0;
741       }
742 /*      out_err(LEVEL_HIGH,"user is in others : %d !\n",ret);*/
743       free_file_recursive(file_list);
744       file_list = NULL;
745       return !ret;
746 
747     }
748 
749     /* NOTE all results are inverted (!=) because we return 0 on success ! */
750     switch (wanted_right) {
751     case RIGHT_RETR:
752       ret = (acl_cur->perms[0]!='r');
753       break;
754     case RIGHT_STOR:
755       ret = (acl_cur->perms[1]!='w');
756       break;
757     case RIGHT_CWD:
758       if (is_dir) ret = (acl_cur->perms[2]!='x');
759       else ret = -1;
760       break;
761     case RIGHT_LIST:
762       if (is_dir) ret = (acl_cur->perms[0]!='r');
763       else ret = -1;
764       break;
765     case RIGHT_RNFR:
766       ret = (acl_cur->perms[1]!='w');
767       break;
768     default:
769       ret = -1; /* stupid right asked */
770       break;
771     }
772     free_file_recursive(file_list);
773     file_list = NULL;
774     return ret; /* stupid right asked */
775   } else { /* ! in file_list */
776     /* FIXME XXX search in parent dirs ???????? - group perms XXX FIXME */
777     free_file_recursive(file_list);
778     file_list = NULL;
779     return _default_perm(wanted_right,user);
780   } /* ! in acl */
781 
782 }
783 
784 /** MUST NOT be / terminated (except /) */
_checkPerm(const char * filename,unsigned long wanted_right,wzd_user_t * user)785 int _checkPerm(const char *filename, unsigned long wanted_right, wzd_user_t * user)
786 {
787   char dir[WZD_MAX_PATH+1];
788   char stripped_filename[WZD_MAX_PATH+1];
789   char *ptr;
790   fs_filestat_t s;
791 
792   if (!filename || filename[0] == '\0')
793     return -1;
794 
795 #ifdef WZD_DBG_PERMS
796   out_err(LEVEL_HIGH,"_checkPerm(%s,%ld,%s)\n",filename,wanted_right,user->username);
797 #endif
798 
799   strncpy(dir,filename,WZD_MAX_PATH);
800 
801   if (user->flags && strchr(user->flags,FLAG_ANONYMOUS))
802   {
803     switch (wanted_right) {
804       case RIGHT_STOR:
805       case RIGHT_MKDIR:
806       case RIGHT_RMDIR:
807       case RIGHT_RNFR:
808         return -1;
809     }
810   }
811 
812   if (fs_file_stat(filename,&s)==-1) {
813     if (wanted_right != RIGHT_STOR && wanted_right != RIGHT_MKDIR)
814       return -1; /* inexistant ? */
815     ptr = strrchr(dir,'/');
816 #ifdef WIN32
817     if ( (ptr-dir)==2 && dir[1]==':' )
818       ptr++;
819 #endif
820     if (ptr) {
821       strcpy(stripped_filename,ptr+1);
822       if (ptr == &dir[0]) *(ptr+1) = '\0';
823       else *ptr = 0;
824     }
825     /* we need to check in parent dir for the same right */
826     if (_checkPerm(dir,wanted_right,user)) return -1; /* we do not have the right to modify parent dir */
827   } else {
828     if (S_ISDIR(s.mode)) { /* isdir */
829       strcpy(stripped_filename,".");
830     } else { /* ! isdir */
831       ptr = strrchr(dir,'/');
832       if (ptr) {
833         strcpy(stripped_filename,ptr+1);
834         if (ptr == &dir[0]) *(ptr+1) = '\0';
835         else *ptr = 0;
836       }
837     } /* ! isdir */
838   } /* stat == -1 */
839 
840   if (dir[strlen(dir)-1] != '/') {
841     strcat(dir,"/");
842   }
843 
844   /** \bug we need to find a way to know if file is in 'visible' path of user.
845    * We can't do that without a function to convert syspath to ftppath
846    */
847 #if 0 /* checkpath_new already checks that */
848   /* check if file is in user's root path */
849   if (strncmp(dir,user->rootpath,strlen(user->rootpath))!=0)
850   {
851     /* if the file is in a global vfs, it does not need to be in user's rootpath */
852     /** \bug it can be a symlink ! */
853     wzd_vfs_t * vfs = mainConfig->vfs;
854     while(vfs) {
855       if (strncmp(dir,vfs->physical_dir,strlen(vfs->physical_dir))==0)
856         return _checkFileForPerm(dir,stripped_filename,wanted_right,user);
857       vfs = vfs->next_vfs;
858     }
859     /* if dir is not a vfs, we can have a vfile */
860     vfs = mainConfig->vfs;
861     while(vfs) {
862       if (DIRCMP(filename,vfs->physical_dir)==0)
863         return _checkFileForPerm(dir,stripped_filename,wanted_right,user);
864       vfs = vfs->next_vfs;
865     }
866     return 1;
867   }
868 #endif
869 
870   return _checkFileForPerm(dir,stripped_filename,wanted_right,user);
871 }
872 
873 /** MUST NOT be / terminated (except /) */
_setPerm(const char * filename,const char * granted_user,const char * owner,const char * group,const char * rights,unsigned long perms,wzd_context_t * context)874 int _setPerm(const char *filename, const char *granted_user, const char *owner, const char *group, const char * rights, unsigned long perms, wzd_context_t * context)
875 {
876   char dir[WZD_MAX_PATH+1];
877   char stripped_filename[WZD_MAX_PATH+1];
878   char perm_filename[WZD_MAX_PATH+1];
879   char *ptr;
880   fs_filestat_t s;
881   size_t length, neededlength;
882   struct wzd_file_t * file_list=NULL, * file_cur;
883   int ret;
884 
885   if (!filename || filename[0] == '\0')
886     return -1;
887 
888   strncpy(dir,filename,WZD_MAX_PATH);
889 
890   if (fs_file_stat(filename,&s)==-1) return -1; /* inexistant ? */
891   if (S_ISDIR(s.mode)) { /* isdir */
892     strcpy(stripped_filename,".");
893   } else { /* ! isdir */
894     ptr = strrchr(dir,'/');
895     if (ptr) {
896       strcpy(stripped_filename,ptr+1);
897       *ptr = 0;
898     }
899   } /* ! isdir */
900 
901   if (dir[strlen(dir)-1] != '/') {
902     strcat(dir,"/");
903   }
904 
905   /* find the dir containing the perms file */
906   strncpy(perm_filename,dir,WZD_MAX_PATH);
907   neededlength = strlen(HARD_PERMFILE);
908   length = strlen(perm_filename);
909   /* check if !overflow */
910   if ( length+neededlength >= WZD_MAX_PATH )
911       return -1;
912 
913   strncpy(perm_filename+length,HARD_PERMFILE,neededlength);
914 
915 
916 #ifdef WZD_DBG_PERMS
917   out_err(LEVEL_FLOOD,"_setPerm: dir %s filename %s wanted file %s\n",dir,perm_filename,stripped_filename);
918 #endif
919 
920   WZD_MUTEX_LOCK(SET_MUTEX_PERMISSION);
921 
922   ret = readPermFile(perm_filename,&file_list);
923   if (ret) { /* no permissions file */
924     file_cur = add_new_file(stripped_filename,0,0,&file_list);
925   } else { /* permission file */
926     file_cur = find_file(stripped_filename,file_list);
927     if (!file_cur) { /* perm file exists, but does not contains acl concerning filename */
928       file_cur = add_new_file(stripped_filename,0,0,&file_list);
929     }
930   }
931 
932   /* set the owner/group */
933   if (owner || group)
934   {
935     if (owner) strncpy(file_cur->owner,owner,256);
936     if (file_cur->owner[0] == '\0')
937       strcpy(file_cur->owner,"nobody");
938     if (group) strncpy(file_cur->group,group,256);
939     if (file_cur->group[0] == '\0')
940       strcpy(file_cur->group,"nogroup");
941   }
942 
943   /* add the new acl */
944   /* remember addAcl REPLACE existing acl on user is already existing */
945   if (rights)
946     addAcl(stripped_filename,granted_user,rights,file_cur);
947   if (perms != (unsigned long)-1)
948   {
949     file_cur->permissions = perms;
950   }
951 
952   /* finally writes perm file on disk */
953   ret = writePermFile(perm_filename,&file_list);
954 
955   free_file_recursive(file_list);
956   file_list = NULL;
957 
958   WZD_MUTEX_UNLOCK(SET_MUTEX_PERMISSION);
959   return 0;
960 }
961 
962 /** MUST NOT be / terminated (except /) */
_movePerm(const char * oldfilename,const char * newfilename,const char * owner,const char * group,wzd_context_t * context)963 int _movePerm(const char *oldfilename, const char *newfilename, const char *owner, const char *group, wzd_context_t * context)
964 {
965   char dir[BUFFER_LEN];
966   char src_stripped_filename[BUFFER_LEN];
967   char src_perm_filename[BUFFER_LEN];
968   char dst_stripped_filename[BUFFER_LEN];
969   char dst_perm_filename[BUFFER_LEN];
970   char *ptr;
971   fs_filestat_t s,s2;
972   size_t length, neededlength;
973   struct wzd_file_t * src_file_list=NULL, *dst_file_list=NULL,* file_cur, *file_dst;
974   wzd_acl_line_t * acl;
975   int ret;
976 
977   if (!oldfilename || oldfilename[0] == '\0') return -1;
978   if (!newfilename || newfilename[0] == '\0') return -1;
979 
980   /* find src perm file name */
981   strncpy(dir,oldfilename,BUFFER_LEN);
982 
983   if (fs_file_stat(dir,&s)==-1) return -1; /* inexistant ? */
984   if (S_ISDIR(s.mode)) { /* isdir */
985     /* TODO XXX FIXME Check validity of this assertion ! */
986     /* permissions of directory are self contained ! */
987     return 0;
988     strcpy(src_stripped_filename,".");
989   } else { /* ! isdir */
990     ptr = strrchr(dir,'/');
991     if (ptr) {
992       strcpy(src_stripped_filename,ptr+1);
993       *ptr = 0;
994     }
995   } /* ! isdir */
996 
997   if (dir[strlen(dir)-1] != '/') {
998     strcat(dir,"/");
999   }
1000 
1001   /* find the dir containing the perms file */
1002   strncpy(src_perm_filename,dir,BUFFER_LEN);
1003   neededlength = strlen(HARD_PERMFILE);
1004   length = strlen(src_perm_filename);
1005   /* check if !overflow */
1006   if ( length+neededlength > 4095 )
1007       return -1;
1008 
1009   strncpy(src_perm_filename+length,HARD_PERMFILE,neededlength);
1010 
1011   /* find dst perm file name */
1012   strncpy(dir,newfilename,BUFFER_LEN);
1013 
1014   /* if dst file is a dir and exists, we can't make the operation */
1015   if (fs_file_stat(dir,&s2)==0) { /* file exists ? */
1016     if (S_ISDIR(s2.mode)) { /* isdir */
1017       return -1;
1018     }
1019   }
1020 
1021 
1022   if (S_ISDIR(s.mode)) { /* isdir */
1023     strcpy(dst_stripped_filename,".");
1024   } else { /* ! isdir */
1025     ptr = strrchr(dir,'/');
1026     if (ptr) {
1027       strcpy(dst_stripped_filename,ptr+1);
1028       *ptr = 0;
1029     }
1030   } /* ! isdir */
1031 
1032   if (dir[strlen(dir)-1] != '/') {
1033     strcat(dir,"/");
1034   }
1035 
1036   /* find the dir containing the perms file */
1037   strncpy(dst_perm_filename,dir,BUFFER_LEN);
1038   neededlength = strlen(HARD_PERMFILE);
1039   length = strlen(dst_perm_filename);
1040   /* check if !overflow */
1041   if ( length+neededlength > 4095 )
1042       return -1;
1043 
1044   strncpy(dst_perm_filename+length,HARD_PERMFILE,neededlength);
1045 
1046 #ifdef WZD_DBG_PERMS
1047 out_err(LEVEL_FLOOD,"%s:%d\n",__FILE__,__LINE__);
1048 out_err(LEVEL_FLOOD,"dir %s filename %s wanted file %s\n",dir,src_perm_filename,src_stripped_filename);
1049 out_err(LEVEL_FLOOD,"dir %s filename %s wanted file %s\n",dir,dst_perm_filename,dst_stripped_filename);
1050 #endif
1051 
1052   WZD_MUTEX_LOCK(SET_MUTEX_PERMISSION);
1053 
1054   ret = readPermFile(src_perm_filename,&src_file_list);
1055   if (ret) { /* no permissions file */
1056     file_dst = NULL;
1057   } else { /* permission file */
1058     file_dst = remove_file(src_stripped_filename,&src_file_list);
1059   } /* permission file */
1060 
1061   /* finally writes perm file on disk */
1062   ret = writePermFile(src_perm_filename,&src_file_list);
1063   free_file_recursive(src_file_list);
1064   src_file_list = NULL;
1065 
1066   ret = readPermFile(dst_perm_filename,&dst_file_list);
1067 
1068   if (!file_dst) { /* src_file had no acl, so we have to remove acl on dst_file if present, and set owner/group */
1069     file_cur = remove_file(dst_stripped_filename,&dst_file_list);
1070     free_file_recursive(file_cur);
1071   } else {
1072 
1073     if (ret) { /* no permissions file */
1074       file_cur = add_new_file(dst_stripped_filename,file_dst->owner,file_dst->group,&dst_file_list);
1075     } else { /* permission file */
1076       file_cur = find_file(dst_stripped_filename,dst_file_list);
1077       if (!file_cur) { /* perm file exists, but does not contains acl concerning filename */
1078         file_cur = add_new_file(dst_stripped_filename,file_dst->owner,file_dst->group,&dst_file_list);
1079       } else {
1080         if (owner) strncpy(file_cur->owner,file_dst->owner,256);
1081         if (group) strncpy(file_cur->group,file_dst->group,256);
1082       }
1083     }
1084 
1085     /* replace the new acl */
1086     acl = file_cur->acl;
1087     file_cur->acl = file_dst->acl;
1088     file_dst->acl = acl;
1089 
1090     free_file_recursive(file_dst);
1091 
1092   } /* if file_dst */
1093 
1094   /* finally writes perm file on disk */
1095   ret = writePermFile(dst_perm_filename,&dst_file_list);
1096 
1097   free_file_recursive(dst_file_list);
1098   dst_file_list = NULL;
1099 
1100   WZD_MUTEX_UNLOCK(SET_MUTEX_PERMISSION);
1101   return 0;
1102 }
1103 
1104 #ifdef _MSC_VER
1105 
_rdb_init(TMN_REPARSE_DATA_BUFFER * rdb,LPCWSTR wszJunctionPoint)1106 int _rdb_init(TMN_REPARSE_DATA_BUFFER * rdb, LPCWSTR wszJunctionPoint)
1107 {
1108   size_t nDestMountPointBytes;
1109 
1110   if (!wszJunctionPoint || !*wszJunctionPoint) {
1111     return -1;
1112   }
1113 
1114   nDestMountPointBytes = lstrlenW(wszJunctionPoint) * 2;
1115 
1116   rdb->ReparseTag           = IO_REPARSE_TAG_MOUNT_POINT;
1117   rdb->ReparseDataLength    = nDestMountPointBytes + 12;
1118   rdb->Reserved             = 0;
1119   rdb->SubstituteNameOffset = 0;
1120   rdb->SubstituteNameLength = nDestMountPointBytes;
1121   rdb->PrintNameOffset      = nDestMountPointBytes + 2;
1122   rdb->PrintNameLength      = 0;
1123   lstrcpyW(rdb->PathBuffer, wszJunctionPoint);
1124 
1125   return 0;
1126 }
1127 
1128 /* returns 0 if ok */
RDB_INIT(TMN_REPARSE_DATA_BUFFER * rdb,LPCSTR szJunctionPoint)1129 int RDB_INIT(TMN_REPARSE_DATA_BUFFER * rdb, LPCSTR szJunctionPoint)
1130 {
1131   wchar_t wszDestMountPoint[512];
1132   size_t cchDest;
1133 
1134   if (!szJunctionPoint || !*szJunctionPoint) {
1135     return -1;
1136   }
1137 
1138   cchDest = lstrlenA(szJunctionPoint) + 1;
1139   if (cchDest > 512) {
1140     return -1;
1141   }
1142 
1143   if (!MultiByteToWideChar(CP_THREAD_ACP,
1144         MB_PRECOMPOSED,
1145         szJunctionPoint,
1146         cchDest,
1147         wszDestMountPoint,
1148         cchDest))
1149   {
1150     return -1;
1151   }
1152 
1153   return _rdb_init(rdb,wszDestMountPoint);
1154 }
1155 
BytesForIoControl(const TMN_REPARSE_DATA_BUFFER * rdb)1156 int BytesForIoControl(const TMN_REPARSE_DATA_BUFFER *rdb)
1157 {
1158   return rdb->ReparseDataLength + TMN_REPARSE_DATA_BUFFER_HEADER_SIZE;
1159 }
1160 
Reparse_Dir_HANDLE(LPCTSTR szDir,int bWriteable)1161 HANDLE Reparse_Dir_HANDLE(LPCTSTR szDir, int bWriteable)
1162 {
1163   return CreateFile(	szDir,
1164       GENERIC_READ | (bWriteable ? GENERIC_WRITE : 0),
1165       0,
1166       0,
1167       OPEN_EXISTING,
1168       FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
1169       0);
1170 }
1171 
1172 /* returns 0 if failed, non-zero if success */
SetReparsePoint(HANDLE m_hDir,const TMN_REPARSE_DATA_BUFFER * rdb)1173 int SetReparsePoint(HANDLE m_hDir, const TMN_REPARSE_DATA_BUFFER* rdb)
1174 {
1175   DWORD dwBytes;
1176   return DeviceIoControl(m_hDir,
1177       FSCTL_SET_REPARSE_POINT,
1178       (LPVOID)rdb,
1179       BytesForIoControl(rdb),
1180       NULL,
1181       0,
1182       &dwBytes,
1183       0);
1184 }
1185 
DeleteReparsePoint(HANDLE m_hDir)1186 int DeleteReparsePoint(HANDLE m_hDir)
1187 {
1188   REPARSE_GUID_DATA_BUFFER rgdb = { 0 };
1189   DWORD dwBytes;
1190   rgdb.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
1191   return DeviceIoControl(m_hDir,
1192       FSCTL_DELETE_REPARSE_POINT,
1193       &rgdb,
1194       REPARSE_GUID_DATA_BUFFER_HEADER_SIZE,
1195       NULL,
1196       0,
1197       &dwBytes,
1198       0);
1199 }
1200 
CreateJunctionPoint(LPCTSTR szMountDir,LPCTSTR szDestDirArg)1201 int CreateJunctionPoint(LPCTSTR szMountDir, LPCTSTR szDestDirArg)
1202 {
1203   TCHAR szDestDir[1024];
1204   char szBuff[MAXIMUM_REPARSE_DATA_BUFFER_SIZE] = { 0 };
1205   TMN_REPARSE_DATA_BUFFER * rdb;
1206   TCHAR szFullDir[1024];
1207   LPTSTR pFilePart;
1208 
1209   if (!szMountDir || !szDestDirArg || !szMountDir[0] || !szDestDirArg[0]) {
1210     return -1;
1211   }
1212 
1213   if (szDestDirArg[0] == '\\' && szDestDirArg[1] == '?') {
1214     lstrcpy(szDestDir, szDestDirArg);
1215   } else {
1216     lstrcpy(szDestDir, TEXT("\\??\\"));
1217     if (!GetFullPathName(szDestDirArg, 1024, szFullDir, &pFilePart) ||
1218         GetFileAttributes(szFullDir) == -1)
1219     {
1220       return -1;
1221     }
1222     lstrcat(szDestDir, szFullDir);
1223   }
1224 
1225   if (!GetFullPathName(szMountDir, 1024, szFullDir, &pFilePart) )
1226   {
1227     return -1;
1228   }
1229   szMountDir = szFullDir;
1230 
1231   // create link if not existing
1232   CreateDirectory(szMountDir, NULL);
1233 
1234   rdb = (TMN_REPARSE_DATA_BUFFER*)szBuff;
1235 
1236   RDB_INIT(rdb,szDestDir);
1237 
1238   {
1239     HANDLE handle;
1240     handle = Reparse_Dir_HANDLE(szMountDir, 1 /* true */);
1241     if (handle == INVALID_HANDLE_VALUE) { CloseHandle(handle); RemoveDirectory(szMountDir); return -1; }
1242     if (!SetReparsePoint(handle,rdb)) { CloseHandle(handle); RemoveDirectory(szMountDir); return -1; }
1243     CloseHandle(handle);
1244   }
1245 
1246 
1247   return 0;
1248 }
1249 
RemoveJunctionPoint(LPCTSTR szDir)1250 int RemoveJunctionPoint(LPCTSTR szDir)
1251 {
1252   TCHAR szFullDir[1024];
1253   LPTSTR pFilePart;
1254 
1255   if (!szDir || !szDir[0]) {
1256     return -1;
1257   }
1258 
1259   if (!GetFullPathName(szDir, 1024, szFullDir, &pFilePart) )
1260   {
1261     return -1;
1262   }
1263   szDir = szFullDir;
1264 
1265   {
1266     HANDLE handle;
1267     handle = Reparse_Dir_HANDLE(szDir, 1 /* true */);
1268     if (handle == INVALID_HANDLE_VALUE) { CloseHandle(handle); return -1; }
1269     if (!DeleteReparsePoint(handle)) { CloseHandle(handle); return -1; }
1270     CloseHandle(handle);
1271     RemoveDirectory(szDir);
1272   }
1273 
1274 
1275   return 0;
1276 }
1277 
1278 
1279 
1280 
1281 #endif
1282 
softlink_create(const char * target,const char * linkname)1283 int softlink_create(const char *target, const char *linkname)
1284 {
1285   char perm_filename[WZD_MAX_PATH];
1286   char stripped_filename[WZD_MAX_PATH];
1287   char *ptr;
1288   struct wzd_file_t * perm_list=NULL, * file_cur;
1289   int ret;
1290   fs_filestat_t s;
1291 
1292   if (fs_file_stat(target,&s)) { /* target does not exist ?! */
1293     out_err(LEVEL_FLOOD, "symlink: source does not exist (%s)\n", target);
1294     return -1;
1295   }
1296   if (fs_file_stat(linkname,&s) != -1) { /* linkname already exist ?! */
1297     out_err(LEVEL_FLOOD, "symlink: destination already exists (%s)\n", linkname);
1298     return -1;
1299   }
1300 
1301   /* get permission file */
1302   strncpy(perm_filename,linkname,WZD_MAX_PATH);
1303   REMOVE_TRAILING_SLASH(perm_filename);
1304 
1305   ptr = strrchr(perm_filename,'/');
1306   if (!ptr) return -1;
1307   {
1308     /* check that dir exist */
1309     if (ptr != perm_filename
1310 #ifdef WIN32
1311       && ptr[-1] != ':'
1312 #endif
1313       )
1314     {
1315       *ptr = '\0';
1316       if (fs_file_stat(perm_filename,&s)) {
1317         out_err(LEVEL_FLOOD, "symlink: destination directory does not exist (%s)\n", perm_filename);
1318         return -1;
1319       }
1320       *ptr = '/';
1321     }
1322   }
1323   ptr++; /* position is just after last / */
1324   strncpy(stripped_filename, ptr, WZD_MAX_PATH);
1325   strncpy(ptr, HARD_PERMFILE, WZD_MAX_PATH - (ptr-perm_filename));
1326 
1327   WZD_MUTEX_LOCK(SET_MUTEX_PERMISSION);
1328   /* read perm file */
1329   ret = readPermFile(perm_filename,&perm_list);
1330 
1331   /* create new entry */
1332   if (ret) { /* no permission file */
1333     file_cur = add_new_file(stripped_filename, 0, 0, &perm_list);
1334   } else {
1335     file_cur = find_file(stripped_filename, perm_list);
1336     if (file_cur) {
1337       /* error, an entry already exists with the same name */
1338       out_err(LEVEL_FLOOD, "symlink: link already exists here (%s)\n", perm_filename);
1339       free_file_recursive(perm_list);
1340       WZD_MUTEX_UNLOCK(SET_MUTEX_PERMISSION);
1341       return EEXIST;
1342     }
1343     file_cur = add_new_file(stripped_filename, 0, 0, &perm_list);
1344   }
1345 
1346   file_cur->kind = FILE_LNK;
1347   file_cur->data = strdup(target);
1348   REMOVE_TRAILING_SLASH( (char*) file_cur->data );
1349 
1350   /** \todo set owner/group of symlink ? */
1351   strncpy(file_cur->owner,"nobody",256);
1352   strncpy(file_cur->group,"nogroup",256);
1353 
1354   /* write modified permission file on disk */
1355   ret = writePermFile(perm_filename, &perm_list);
1356 
1357   free_file_recursive(perm_list);
1358   perm_list = NULL;
1359   WZD_MUTEX_UNLOCK(SET_MUTEX_PERMISSION);
1360 
1361   return 0;
1362 }
1363 
softlink_remove(const char * linkname)1364 int softlink_remove(const char *linkname)
1365 {
1366   char perm_filename[WZD_MAX_PATH];
1367   char stripped_filename[WZD_MAX_PATH];
1368   char *ptr;
1369   size_t length;
1370   struct wzd_file_t * perm_list=NULL, * file_cur;
1371   int ret;
1372 
1373   /* get permission file */
1374   if (!linkname) return -1;
1375 
1376   strncpy(perm_filename,linkname,WZD_MAX_PATH);
1377   length = strlen(perm_filename);
1378   if (length > 1 && perm_filename[length-1] == '/') perm_filename[--length] = '\0';
1379 
1380   ptr = strrchr(perm_filename,'/');
1381   if (!ptr) return -1;
1382   ptr++; /* position is just after last / */
1383   strncpy(stripped_filename, ptr, WZD_MAX_PATH);
1384   strncpy(ptr, HARD_PERMFILE, WZD_MAX_PATH - (ptr-perm_filename));
1385 
1386   WZD_MUTEX_LOCK(SET_MUTEX_PERMISSION);
1387   /* read perm file */
1388   ret = readPermFile(perm_filename,&perm_list);
1389 
1390   /* remove entry */
1391   if (!ret) {
1392     file_cur = find_file(stripped_filename, perm_list);
1393     if ( !file_cur || file_cur->kind != FILE_LNK )
1394     {
1395       free_file_recursive(perm_list);
1396       out_err(LEVEL_FLOOD, "symlink: trying to remove something that is not a link (%s)\n", linkname);
1397       WZD_MUTEX_UNLOCK(SET_MUTEX_PERMISSION);
1398       return -1;
1399     }
1400 
1401     file_cur = remove_file(stripped_filename, &perm_list);
1402 
1403     /* write modified permission file on disk */
1404     ret = writePermFile(perm_filename, &perm_list);
1405 
1406     free_file_recursive(file_cur);
1407     free_file_recursive(perm_list);
1408   }
1409 
1410   perm_list = NULL;
1411   WZD_MUTEX_UNLOCK(SET_MUTEX_PERMISSION);
1412 
1413   return 0;
1414 }
1415 
1416 
1417 /************ PUBLIC FUNCTIONS ***************/
1418 
file_open(const char * filename,int mode,unsigned long wanted_right,wzd_context_t * context)1419 int file_open(const char *filename, int mode, unsigned long wanted_right, wzd_context_t * context)
1420 {
1421   int fd;
1422   int ret;
1423   wzd_user_t * user;
1424   short is_locked;
1425 
1426   user = GetUserByID(context->userid);
1427 
1428   if (mode & O_WRONLY)
1429     ret = _checkPerm(filename,RIGHT_STOR,user);
1430   else
1431     ret = _checkPerm(filename,RIGHT_RETR,user);
1432   if (ret)
1433     return -1;
1434 
1435   /* is a directory ? */
1436   {
1437     fs_filestat_t s;
1438     if (fs_file_stat(filename,&s) == 0) {
1439       if (S_ISDIR(s.mode)) return -1;
1440     }
1441   }
1442 
1443 #ifdef WIN32
1444   mode |= _O_BINARY;
1445 #endif
1446 
1447   fd = fs_open(filename,mode,0666);
1448   if (fd == -1) {
1449     out_log(LEVEL_INFO,"Can't open %s,errno %d : %s\n",filename,errno,strerror(errno));
1450     return -1;
1451   }
1452 
1453   is_locked = file_islocked(fd,F_WRLCK);
1454 
1455   if (is_locked == -1) {
1456     out_log(LEVEL_NORMAL,"Could not get lock info\n");
1457   }
1458   else {
1459     if ( mode & O_WRONLY ) {
1460       if (is_locked) {
1461         close(fd);
1462 /*        out_err(LEVEL_HIGH,"Can't open %s in write mode, locked !\n",filename);*/
1463         return -1;
1464       }
1465       file_lock(fd,F_WRLCK);
1466     }
1467     else {
1468       if (is_locked) {
1469 /*	out_err(LEVEL_HIGH,"%s is locked, trying to read\n",filename);*/
1470         if ( CFG_GET_OPTION(mainConfig,CFG_OPT_DENY_ACCESS_FILES_UPLOADED) ) {
1471           close(fd);
1472           return -1;
1473         }
1474       }
1475     }
1476   }
1477 
1478   return fd;
1479 }
1480 
file_close(int fd,wzd_context_t * context)1481 void file_close(int fd, wzd_context_t * context)
1482 {
1483   close(fd);
1484 }
1485 
file_seek(fd_t fd,fs_off_t offset,int whence)1486 fs_off_t file_seek(fd_t fd, fs_off_t offset, int whence)
1487 {
1488   return fs_lseek(fd,offset,whence);
1489 }
1490 
1491 /** NOTE:
1492  * one of username/groupname can be NULL
1493  * context is usefull to check if the user can chown to other users
1494  */
file_chown(const char * filename,const char * username,const char * groupname,wzd_context_t * context)1495 int file_chown(const char *filename, const char *username, const char *groupname, wzd_context_t * context)
1496 {
1497   return _setPerm(filename,0,username,groupname,0,(unsigned long)-1,context);
1498 }
1499 
file_mkdir(const char * dirname,unsigned int mode,wzd_context_t * context)1500 int file_mkdir(const char *dirname, unsigned int mode, wzd_context_t * context)
1501 {
1502   int ret;
1503   int err;
1504   wzd_user_t * user;
1505 
1506   user = GetUserByID(context->userid);
1507 
1508   ret = _checkPerm(dirname,RIGHT_MKDIR,user);
1509   if (ret) return E_NOPERM;
1510   ret = fs_mkdir(dirname,0755,&err);
1511 
1512   return (ret) ? E_COMMAND_FAILED : E_OK;
1513 }
1514 
1515 /** @brief remove directory.
1516  *
1517  * dirname must be an absolute path
1518  */
file_rmdir(const char * dirname,wzd_context_t * context)1519 int file_rmdir(const char *dirname, wzd_context_t * context)
1520 {
1521   int ret;
1522   wzd_user_t * user;
1523   fs_filestat_t s;
1524   fs_dir_t * dir;
1525   fs_fileinfo_t * finfo;
1526 
1527   user = GetUserByID(context->userid);
1528 
1529   ret = _checkPerm(dirname,RIGHT_RMDIR,user);
1530   if (ret) return -1;
1531 
1532   /* is a directory ? */
1533   if (fs_file_stat(dirname,&s)) return -1;
1534   if (!S_ISDIR(s.mode)) return -1;
1535 
1536   /* is dir empty ? */
1537   {
1538     char path_perm[2048];
1539     const char *filename;
1540 
1541     if ( fs_dir_open(dirname,&dir) ) return 0;
1542 
1543     while ( !fs_dir_read(dir,&finfo) ) {
1544       filename = fs_fileinfo_getname(finfo);
1545 
1546       if (strcmp(filename,".")==0 ||
1547           strcmp(filename,"..")==0 ||
1548           strcmp(filename,HARD_PERMFILE)==0) /* XXX hide perm file ! */
1549         continue;
1550       fs_dir_close(dir);
1551       return 1; /* dir not empty */
1552     }
1553 
1554     fs_dir_close(dir);
1555 
1556     /* remove permission file */
1557     strcpy(path_perm,dirname); /* path is already ended by / */
1558     if (path_perm[strlen(path_perm)-1] != '/')
1559       strcat(path_perm,"/");
1560     (void)strlcat(path_perm,HARD_PERMFILE,sizeof(path_perm));
1561     unlink(path_perm);
1562   }
1563 
1564 #ifdef DEBUG
1565 out_err(LEVEL_HIGH,"Removing directory '%s'\n",dirname);
1566 #endif
1567 
1568 #ifndef __CYGWIN__
1569   {
1570     fs_filestat_t s;
1571     fs_file_lstat(dirname,&s);
1572     if (S_ISLNK(s.mode))
1573       return unlink(dirname);
1574   }
1575 #endif
1576   return rmdir(dirname);
1577 
1578 }
1579 
1580 /** \brief Change the name or location of a file
1581  *
1582  * old_filename and new_filename must be ABSOLUTE paths
1583  */
file_rename(const char * old_filename,const char * new_filename,wzd_context_t * context)1584 int file_rename(const char *old_filename, const char *new_filename, wzd_context_t * context)
1585 {
1586   char path[2048];
1587   char * ptr;
1588   int ret;
1589   wzd_user_t * user;
1590 
1591   user = GetUserByID(context->userid);
1592 
1593   strncpy(path,new_filename,2048);
1594   ptr = strrchr(path,'/');
1595   if (!ptr) return 1;
1596   *ptr = '\0';
1597   ret = _checkPerm(old_filename,RIGHT_RNFR,user);
1598   ret = ret || _checkPerm(path,RIGHT_STOR,user);
1599   if (ret)
1600     return 1;
1601 
1602   /* change file name in perm file !! */
1603   ret = _movePerm(old_filename,new_filename,0,0,context);
1604 
1605   ret = safe_rename(old_filename,new_filename);
1606   if (ret==-1) {
1607 #ifdef DEBUG
1608 out_err(LEVEL_HIGH,"rename error %d (%s)\n", errno, strerror(errno));
1609 #endif
1610     return 1;
1611   }
1612 
1613   return 0;
1614 }
1615 
file_remove(const char * filename,wzd_context_t * context)1616 int file_remove(const char *filename, wzd_context_t * context)
1617 {
1618   char perm_filename[BUFFER_LEN];
1619   char stripped_filename[BUFFER_LEN];
1620   char * ptr;
1621   int ret;
1622   wzd_user_t * user;
1623   struct wzd_file_t * file_list=NULL, * file_cur;
1624   size_t neededlength, length;
1625 
1626   /* find the dir containing the perms file */
1627   strncpy(perm_filename,filename,BUFFER_LEN);
1628   ptr = strrchr(perm_filename,'/');
1629   if (!ptr || *(ptr+1)=='\0') return -1;
1630   strcpy(stripped_filename,ptr+1);
1631   if (ptr != perm_filename) *(ptr+1)='\0';
1632   neededlength = strlen(HARD_PERMFILE);
1633   length = strlen(perm_filename);
1634   /* check if !overflow */
1635   if ( length+neededlength > 4095 )
1636       return -1;
1637 
1638   strncpy(perm_filename+length,HARD_PERMFILE,neededlength);
1639   perm_filename[length+neededlength]='\0';
1640 
1641   user = GetUserByID(context->userid);
1642 
1643 /*  ret = _checkPerm(filename,RIGHT_STOR ,user);*/
1644   /* to delete, defaults permissions are: owner and siteop can delete file */
1645   if (user->flags && strchr(user->flags,FLAG_SITEOP))
1646     ret = 0; /* siteop -> ok */
1647   else
1648   {
1649     ret = 1;
1650     file_cur = file_stat(filename, context);
1651 
1652     /* if file_cur is NULL it means that we have no entry for that file
1653      * it happens when deleting a symlink, when destination does not
1654      * exist
1655      */
1656     if (file_cur) {
1657       if (strcmp(user->username, file_cur->owner)==0) ret = 0; /* owner */
1658 
1659       free_file_recursive(file_cur);
1660       file_cur = NULL;
1661     }
1662 
1663     /* The delete permission is special: by default, all users can run the DELE
1664      * command, but only owner of the file or siteops can delete files.
1665      * If the "delete" permission is set, it will allow non-owners to delete the file.
1666      */
1667     {
1668       wzd_command_t * command;
1669       wzd_string_t * str = STR("delete");
1670 
1671       command = commands_find(mainConfig->commands_list, str);
1672       str_deallocate(str);
1673       if (commands_check_permission(command, context) == 0) ret = 0;
1674     }
1675 
1676   }
1677 
1678   if (ret)
1679     return 1;
1680 
1681   WZD_MUTEX_LOCK(SET_MUTEX_PERMISSION);
1682   /* remove name in perm file !! */
1683   ret = readPermFile(perm_filename,&file_list);
1684   if (!ret) {
1685     file_cur = remove_file(stripped_filename, &file_list);
1686     ret = writePermFile(perm_filename,&file_list);
1687     free_file_recursive(file_cur);
1688     free_file_recursive(file_list);
1689   }
1690   ret = unlink(filename);
1691   if (ret==-1) {
1692 #ifdef DEBUG
1693 out_err(LEVEL_HIGH,"remove error %d (%s)\n", errno, strerror(errno));
1694 #endif
1695     WZD_MUTEX_UNLOCK(SET_MUTEX_PERMISSION);
1696     return 1;
1697   }
1698   WZD_MUTEX_UNLOCK(SET_MUTEX_PERMISSION);
1699 
1700   return 0;
1701 }
1702 
file_getowner(const char * filename,wzd_context_t * context)1703 wzd_user_t * file_getowner(const char *filename, wzd_context_t * context)
1704 {
1705   char perm_filename[BUFFER_LEN];
1706   char stripped_filename[BUFFER_LEN];
1707   char * ptr;
1708   int ret;
1709   struct wzd_file_t * file_list=NULL, * file_cur;
1710   size_t neededlength, length;
1711   fs_filestat_t s;
1712 
1713   if (fs_file_stat(filename,&s))
1714     return NULL;
1715 
1716   /* find the dir containing the perms file */
1717   strncpy(perm_filename,filename,BUFFER_LEN);
1718   ptr = strrchr(perm_filename,'/');
1719   if (!ptr || *(ptr+1)=='\0') return NULL;
1720 
1721   if (S_ISDIR(s.mode)) { /* isdir */
1722     strcpy(stripped_filename,".");
1723   } else { /* ! isdir */
1724     ptr = strrchr(perm_filename,'/');
1725     if (ptr) {
1726       strcpy(stripped_filename,ptr+1);
1727       *ptr = 0;
1728     }
1729   } /* ! isdir */
1730 
1731 
1732 /*  strcpy(stripped_filename,ptr+1);*/
1733 /*  if (ptr != perm_filename) *(ptr+1)='\0';*/
1734 
1735   neededlength = strlen(HARD_PERMFILE)+1;
1736   length = strlen(perm_filename);
1737   /* check if !overflow */
1738   if ( length+neededlength > 4095 )
1739       return NULL;
1740 
1741   if (perm_filename[length-1] != '/' ) {
1742     ++length;
1743     perm_filename[length-1] = '/';
1744   }
1745   strncpy(perm_filename+length,HARD_PERMFILE,neededlength);
1746 
1747   /* remove name in perm file !! */
1748   ret = readPermFile(perm_filename,&file_list);
1749   if (!ret) {
1750     /* we have a permission file */
1751     file_cur = file_list;
1752     while (file_cur)
1753     {
1754       if (strcmp(stripped_filename,file_cur->filename)==0) {
1755         if (file_cur->owner[0]!='\0')
1756         {
1757           wzd_user_t * user;
1758           user = GetUserByName(file_cur->owner);
1759           free_file_recursive(file_list);
1760           return user;
1761         }
1762         else
1763         {
1764           free_file_recursive(file_list);
1765           return GetUserByName("nobody");
1766         }
1767       }
1768       file_cur = file_cur->next_file;
1769     }
1770     free_file_recursive(file_list);
1771   }
1772 
1773   return GetUserByName("nobody");
1774 }
1775 
1776 /** Permissions are returned as a hex value composed of permissions ORed like
1777  * RIGHT_LIST | RIGHT_CWD
1778  */
file_getperms(struct wzd_file_t * file,wzd_context_t * context)1779 unsigned long file_getperms(struct wzd_file_t * file, wzd_context_t * context)
1780 {
1781   unsigned long perms = 0;
1782   wzd_user_t * user;
1783   wzd_acl_line_t * acl_cur;
1784   wzd_group_t * group;
1785 
1786   WZD_ASSERT(context != NULL);
1787 
1788   user = GetUserByID(context->userid);
1789   if (!user) return RIGHT_NONE;
1790 
1791   if (!file) return _default_perm(0xffffffff,user);
1792 
1793   /* now find corresponding acl */
1794   acl_cur = find_acl(user->username,file);
1795 
1796   if (acl_cur) {
1797     if (acl_cur->perms[0]=='r') perms |= RIGHT_RETR;
1798     if (acl_cur->perms[1]=='w') perms |= RIGHT_STOR | RIGHT_RNFR;
1799     if (file->kind == FILE_DIR && acl_cur->perms[2]=='x') perms |= RIGHT_CWD;
1800   } else { /* no acl, check 'permissions field */
1801     /* owner ? */
1802     if (strcmp(user->username,file->owner)==0) {
1803       if (file->permissions & 0400) perms |= RIGHT_RETR;
1804       if (file->permissions & 0200) perms |= RIGHT_STOR | RIGHT_RNFR;
1805       if (file->kind == FILE_DIR && file->permissions & 0100) perms |= RIGHT_CWD;
1806     } else {
1807       /* same group ? */
1808       unsigned int i;
1809       unsigned short found=0;
1810 
1811       for (i=0; i<user->group_num; i++) {
1812         group = GetGroupByID(user->groups[i]);
1813         if (group && strcmp(group->groupname,file->group)==0) {
1814           found++;
1815           if (file->permissions & 0040) perms |= RIGHT_RETR;
1816           if (file->permissions & 0020) perms |= RIGHT_STOR | RIGHT_RNFR;
1817           if (file->kind == FILE_DIR && file->permissions & 0010) perms |= RIGHT_CWD;
1818         }
1819       }
1820 
1821       if (!found) { /* "others" permissions apply */
1822         if (file->permissions & 0004) perms |= RIGHT_RETR;
1823         if (file->permissions & 0002) perms |= RIGHT_STOR | RIGHT_RNFR;
1824         if (file->kind == FILE_DIR && file->permissions & 0001) perms |= RIGHT_CWD;
1825       }
1826     }
1827   }
1828 
1829   /* is a directory ? */
1830   if (file->kind == FILE_DIR) {
1831     if (perms & RIGHT_RETR) perms |= RIGHT_LIST;
1832     if (perms & RIGHT_STOR) perms |= RIGHT_MKDIR;
1833   }
1834 
1835   /** \todo RIGHT_DELE is never checked */
1836 
1837   return perms;
1838 }
1839 
1840 
1841 /** This function return information about the specified file. You do not need any
1842  * special right on the file, but you need search rights on any directory on the
1843  * path to the file.
1844  *
1845  * If filename is a symbolic link, the destination is stat-ed, not the link itself.
1846  *
1847  * Caller MUST free memory using \ref free_file_recursive
1848  *
1849  * \return struct, or NULL if nothing known, -1 if error or non-existant
1850  */
file_stat(const char * filename,wzd_context_t * context)1851 struct wzd_file_t * file_stat(const char *filename, wzd_context_t * context)
1852 {
1853   char perm_filename[WZD_MAX_PATH+1];
1854   char stripped_filename[WZD_MAX_PATH+1];
1855   char * ptr;
1856   struct wzd_file_t * file_list=NULL, * file_cur, *file;
1857   size_t neededlength, length;
1858   fs_filestat_t s;
1859   int nx=0;
1860 
1861   /** \bug no no no, it can be a symlink or a vfs ! */
1862 /*  if (fs_stat(filename,&s))
1863     return NULL;*/
1864 
1865   /* check for VFS */
1866   {
1867     wzd_vfs_t * vfs = mainConfig->vfs;
1868     char * buffer_vfs;
1869 
1870     while (vfs) {
1871       buffer_vfs = vfs_replace_cookies(vfs->virtual_dir,context);
1872       if (!buffer_vfs) {
1873         out_log(LEVEL_CRITICAL,"vfs_replace_cookies returned NULL for %s\n",vfs->virtual_dir);
1874         vfs = vfs->next_vfs;
1875         continue;
1876       }
1877 
1878       if (DIRCMP(buffer_vfs,filename)==0) {
1879         /* ok, we have a candidate */
1880         file = file_stat(vfs->physical_dir,context);
1881         wzd_free(buffer_vfs);
1882         return file;
1883       }
1884 
1885       wzd_free(buffer_vfs);
1886       vfs = vfs->next_vfs;
1887     }
1888 
1889   }
1890 
1891   file = NULL;
1892 
1893   /* find the dir containing the perms file */
1894   wzd_strncpy(perm_filename,filename,WZD_MAX_PATH);
1895   length = strlen(perm_filename);
1896   if (length >1 && perm_filename[length-1]=='/')
1897     perm_filename[--length] = '\0';
1898   ptr = strrchr(perm_filename,'/');
1899   if (ptr == NULL) return NULL;
1900 
1901   if (!fs_file_lstat(filename,&s)) {
1902     if (S_ISDIR(s.mode)) { /* isdir */
1903       strcpy(stripped_filename,".");
1904     } else { /* ! isdir */
1905       ptr = strrchr(perm_filename,'/');
1906       if (ptr) {
1907         strcpy(stripped_filename,ptr+1);
1908         *ptr = 0;
1909       }
1910     }
1911   } else { /* ! exists */
1912     nx = 1;
1913     ptr = strrchr(perm_filename,'/');
1914     if (ptr) {
1915       strcpy(stripped_filename,ptr+1);
1916       *ptr = 0;
1917       if (fs_file_lstat(perm_filename,&s)) {
1918         out_err(LEVEL_FLOOD, "symlink: destination directory does not exist (%s)\n", perm_filename);
1919         return NULL;
1920       }
1921     }
1922   } /* ! exists */
1923 
1924 
1925 /*  strcpy(stripped_filename,ptr+1);*/
1926 /*  if (ptr != perm_filename) *(ptr+1)='\0';*/
1927 
1928   neededlength = strlen(HARD_PERMFILE)+1;
1929   length = strlen(perm_filename);
1930   /* check if !overflow */
1931   if ( length+neededlength >= WZD_MAX_PATH )
1932       return NULL;
1933 
1934   if (perm_filename[length-1] != '/' ) {
1935     ++length;
1936     perm_filename[length-1] = '/';
1937   }
1938   wzd_strncpy(perm_filename+length,HARD_PERMFILE,neededlength);
1939 
1940   if ( ! readPermFile(perm_filename,&file_list) ) {
1941     /* we have a permission file */
1942     file_cur = find_file(stripped_filename, file_list);
1943     if (file_cur)
1944       file = file_deep_copy(file_cur);
1945     free_file_recursive(file_list);
1946   }
1947 
1948   if (!file && nx) return NULL;
1949 
1950   if (file == NULL) { /* create minimal struct */
1951     /** \bug XXX FIXME we should not allocate anything here, since this will be a memory leak */
1952     file = wzd_malloc(sizeof(struct wzd_file_t));
1953 
1954     wzd_strncpy(file->filename,stripped_filename,sizeof(file->filename));
1955     file->owner[0] = '\0';
1956     file->group[0] = '\0';
1957     file->permissions = mainConfig->umask; /** \todo FIXME default permission */
1958     file->acl = NULL;
1959     file->kind = FILE_NOTSET;
1960     file->data = NULL;
1961     file->next_file = NULL;
1962   }
1963 
1964   if (file) {
1965     if (S_ISDIR(s.mode)) file->kind = FILE_DIR;
1966     if (S_ISLNK(s.mode)) file->kind = FILE_LNK;
1967     if (S_ISREG(s.mode)) file->kind = FILE_REG;
1968   }
1969 
1970   return file;
1971 }
1972 
1973 /* if program crash, locks acquired by fcntl (POSIX) or _locking (VISUAL)
1974  * are released, and then do are less annoying.
1975  */
file_lock(fd_t fd,short lock_mode)1976 int file_lock(fd_t fd, short lock_mode)
1977 {
1978 #ifdef WZD_DBG_LOCK
1979 out_err(LEVEL_HIGH,"Locking file %d\n",fd);
1980 #endif
1981 #ifndef WIN32
1982   struct flock lck;
1983   lck.l_type = lock_mode;
1984   lck.l_whence = SEEK_SET;/* offset l_start from beginning of file */
1985   lck.l_start = 0;
1986   lck.l_len = 0;
1987   if (fcntl(fd, F_SETLK, &lck) < 0) {
1988     return -1;
1989   }
1990 #else
1991   if (_locking(fd, LK_NBLCK, -1) == -1)
1992     return -1;
1993 #endif
1994   return 0;
1995 }
1996 
file_unlock(fd_t fd)1997 int file_unlock(fd_t fd)
1998 {
1999 #ifdef WZD_DBG_LOCK
2000 out_err(LEVEL_HIGH,"Unlocking file %d\n",fd);
2001 #endif
2002 #ifndef WIN32
2003   struct flock lck;
2004   lck.l_type = F_UNLCK;
2005   lck.l_whence = SEEK_SET;/* offset l_start from beginning of file */
2006   lck.l_start = 0;
2007   lck.l_len = 0;
2008   if (fcntl(fd, F_SETLK, &lck) < 0) {
2009     return -1;
2010   }
2011 #else
2012   if (_locking(fd, LK_UNLCK, -1) == -1)
2013     return -1;
2014 #endif
2015   return 0;
2016 }
2017 
file_islocked(fd_t fd,short lock_mode)2018 int file_islocked(fd_t fd, short lock_mode)
2019 {
2020 #ifdef WZD_DBG_LOCK
2021 out_err(LEVEL_HIGH,"Testing lock for file %d\n",fd);
2022 #endif
2023 #ifndef WIN32
2024   struct flock lck;
2025   lck.l_type = lock_mode;
2026   lck.l_whence = SEEK_SET;/* offset l_start from beginning of file */
2027   lck.l_start = 0;
2028   lck.l_len = 0;
2029 
2030   if (fcntl(fd, F_GETLK, &lck) < 0) {
2031     return -1;
2032   }
2033   if (lck.l_type == F_RDLCK || lck.l_type == F_WRLCK) return 1;
2034 #else
2035   if (_locking(fd, LK_NBLCK, -1) != -1) {
2036     _locking(fd, LK_UNLCK, -1);
2037     return 0;
2038   } else {
2039     if (errno == EACCES) return 1;
2040     return -1;
2041   }
2042 #endif
2043   return 0;
2044 }
2045 
file_force_unlock(const char * file)2046 int file_force_unlock(const char *file)
2047 {
2048   int fd;
2049 #ifdef WZD_DBG_LOCK
2050 out_err(LEVEL_HIGH,"Forcing unlock file %s\n",file);
2051 #endif
2052 
2053   fd = open(file,O_RDWR);
2054   if (fd < 0) return -1;
2055 
2056 #ifndef WIN32
2057   {
2058     struct flock lck;
2059     lck.l_type = F_UNLCK;
2060     lck.l_whence = SEEK_SET;/* offset l_start from beginning of file */
2061     lck.l_start = 0;
2062     lck.l_len = 0;
2063     if (fcntl(fd, F_SETLK, &lck) < 0) {
2064       close(fd);
2065       return -1;
2066     }
2067   }
2068 #else
2069   if (_locking(fd, LK_UNLCK, -1) == -1)
2070   {
2071     close(fd);
2072     return -1;
2073   }
2074 #endif
2075   close(fd);
2076   return 0;
2077 }
2078 
2079 /* wrappers just to keep things in same memory zones */
file_read(fd_t fd,void * data,size_t length)2080 ssize_t file_read(fd_t fd,void *data,size_t length)
2081 {
2082   return read(fd,data,length);
2083 }
2084 
file_write(fd_t fd,const void * data,size_t length)2085 ssize_t file_write(fd_t fd,const void *data,size_t length)
2086 {
2087   return write(fd,data,length);
2088 }
2089 
2090 /* symlink operations */
2091 
2092 /** \brief create symlink
2093  * paths must be absolute
2094  * paths must not be / terminated !
2095  * \todo if paths are relative, convert them ?
2096  */
symlink_create(const char * existing,const char * link)2097 int symlink_create(const char *existing, const char *link)
2098 {
2099   /** \todo XXX FIXME check that symlink dest is inside user authorized path */
2100 #ifndef WIN32
2101   return symlink(existing, link);
2102 #else
2103   return softlink_create(existing, link);
2104 /*  return CreateJunctionPoint(link, existing);*/
2105 #endif
2106 }
2107 
symlink_remove(const char * link)2108 int symlink_remove(const char *link)
2109 {
2110 #ifndef WIN32
2111   fs_filestat_t s;
2112 
2113   if (fs_file_lstat(link,&s)) return E_FILE_NOEXIST;
2114   if ( !S_ISLNK(s.mode) ) return E_FILE_TYPE;
2115   return unlink(link);
2116 #else
2117   return softlink_remove(link);
2118 /*  return RemoveJunctionPoint(link);*/
2119 #endif
2120 }
2121 
2122 /** @} */
2123 
2124