OK!
[sfrench/samba-autobuild/.git] / examples / VFS / recycle.c
1 /* 
2  * Auditing VFS module for samba.  Log selected file operations to syslog
3  * facility.
4  *
5  * Copyright (C) 2001, Brandon Stone, Amherst College, <bbstone@amherst.edu>.
6  * Copyright (C) 2002, Jeremy Allison - modified to make a VFS module.
7  * Copyright (C) 2002, Alexander Bokovoy - cascaded VFS adoption,
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *  
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *  
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  */
23
24 #include "config.h"
25 #include <stdio.h>
26 #include <sys/stat.h>
27 #ifdef HAVE_UTIME_H
28 #include <utime.h>
29 #endif
30 #ifdef HAVE_DIRENT_H
31 #include <dirent.h>
32 #endif
33 #include <syslog.h>
34 #ifdef HAVE_FCNTL_H
35 #include <fcntl.h>
36 #endif
37 #include <errno.h>
38 #include <string.h>
39 #include <includes.h>
40 #include <vfs.h>
41  
42 /* VFS operations */
43
44 static struct vfs_ops default_vfs_ops;   /* For passthrough operation */
45 static struct smb_vfs_handle_struct *recycle_handle;
46 static int recycle_unlink(connection_struct *, const char *);
47 static int recycle_connect(struct connection_struct *conn, const char *service, const char *user);
48 static void recycle_disconnect(struct connection_struct *conn);
49
50 static vfs_op_tuple recycle_ops[] = {
51
52         /* Disk operations */
53
54         {recycle_connect,       SMB_VFS_OP_CONNECT,     SMB_VFS_LAYER_OPAQUE},
55         {recycle_disconnect,    SMB_VFS_OP_DISCONNECT,  SMB_VFS_LAYER_OPAQUE},
56
57         /* File operations */
58         
59         {recycle_unlink,        SMB_VFS_OP_UNLINK,      SMB_VFS_LAYER_OPAQUE},
60         
61         {NULL,                  SMB_VFS_OP_NOOP,        SMB_VFS_LAYER_NOOP}
62 };
63
64 /* VFS initialisation function.  Return initialised vfs_op_tuple array back to SAMBA. */
65
66 vfs_op_tuple *vfs_init(int *vfs_version, struct vfs_ops *def_vfs_ops,
67                         struct smb_vfs_handle_struct *vfs_handle)
68 {
69         *vfs_version = SMB_VFS_INTERFACE_VERSION;
70         memcpy(&default_vfs_ops, def_vfs_ops, sizeof(struct vfs_ops));
71         
72         /* Remember vfs_id for storing private information at connect */
73         recycle_handle = vfs_handle;
74
75         return recycle_ops;
76 }
77
78 /* VFS finalization function. */
79 void vfs_done(connection_struct *conn)
80 {
81         DEBUG(3,("vfs_done_recycle: called for connection %p\n",conn));
82 }
83
84 static int recycle_connect(struct connection_struct *conn, const char *service, const char *user)
85 {
86         fstring recycle_bin;
87
88         DEBUG(3,("recycle_connect: called for service %s as user %s\n", service, user));
89
90         fstrcpy(recycle_bin, (const char *)lp_parm_string(lp_servicename(SNUM(conn)),"vfs","recycle bin"));
91         if (!*recycle_bin) {
92                 DEBUG(3,("recycle_connect: No options listed (vfs:recycle bin).\n" ));
93                 return 0; /* No options. */
94         }
95
96         DEBUG(3,("recycle_connect: recycle name is %s\n", recycle_bin ));
97
98         recycle_handle->data = (void *)strdup(recycle_bin);
99         return 0;
100 }
101
102 static void recycle_disconnect(struct connection_struct *conn)
103 {
104         SAFE_FREE(recycle_handle->data);
105 }
106
107 static BOOL recycle_XXX_exist(connection_struct *conn, const char *dname, BOOL isdir)
108 {
109         SMB_STRUCT_STAT st;
110
111         if (default_vfs_ops.stat(conn,dname,&st) != 0)
112                 return(False);
113
114         if (isdir)
115                 return S_ISDIR(st.st_mode) ? True : False;
116         else
117                 return S_ISREG(st.st_mode) ? True : False;
118 }
119
120 static BOOL recycle_directory_exist(connection_struct *conn, const char *dname)
121 {
122         return recycle_XXX_exist(conn, dname, True);
123 }
124
125 static BOOL recycle_file_exist(connection_struct *conn, const char *fname)
126 {
127         return recycle_XXX_exist(conn, fname, False);
128 }
129
130 static SMB_OFF_T recycle_get_file_size(connection_struct *conn, const char *fname)
131 {
132         SMB_STRUCT_STAT st;
133
134         if (default_vfs_ops.stat(conn,fname,&st) != 0)
135                 return (SMB_OFF_T)-1;
136
137         return(st.st_size);
138 }
139
140 /********************************************************************
141  Check if file should be recycled
142 *********************************************************************/
143
144 static int recycle_unlink(connection_struct *conn, const char *inname)
145 {
146         fstring recycle_bin;
147         pstring fname;
148         char *base, *ext;
149         pstring bin;
150         int i=1, len, addlen;
151         int dir_mask=0770;
152         SMB_BIG_UINT dfree,dsize,bsize;
153
154         *recycle_bin = '\0';
155         pstrcpy(fname, inname);
156
157         if (recycle_handle->data)
158                 fstrcpy(recycle_bin, (const char *)recycle_handle->data);
159
160         if(!*recycle_bin) {
161                 DEBUG(3, ("recycle bin: share parameter not set, purging %s...\n", fname));
162                 return default_vfs_ops.unlink(conn,fname);
163         }
164
165         if(recycle_get_file_size(conn, fname) == 0) {
166                 DEBUG(3, ("recycle bin: file %s is empty, purging...\n", fname));
167                 return default_vfs_ops.unlink(conn,fname);
168         }
169
170         base = strrchr(fname, '/');
171         pstrcpy(bin, recycle_bin);
172         pstrcat(bin, "/");
173
174         if(base == NULL) {
175                 ext = strrchr(fname, '.');
176                 pstrcat(bin, fname);
177         } else {
178                 ext = strrchr(base, '.');
179                 pstrcat(bin, base+1);
180         }
181         DEBUG(3, ("recycle bin: base %s, ext %s, fname %s, bin %s\n", base, ext, fname, bin));
182
183         if(strcmp(fname,bin) == 0) {
184                 DEBUG(3, ("recycle bin: file %s exists, purging...\n", fname));
185                 return default_vfs_ops.unlink(conn,fname);
186         }
187
188         len = strlen(bin);
189         if ( ext != NULL)
190                 len = len - strlen(ext);
191
192         addlen = sizeof(pstring)-len-1;
193         while(recycle_file_exist(conn,bin)) {
194                 slprintf(bin+len, addlen, " (Copy #%d)", i++);
195                 pstrcat(bin, ext);
196         }
197
198         DEBUG(3, ("recycle bin: moving source=%s to  dest=%s\n", fname, bin));
199         default_vfs_ops.disk_free(conn,".",True,&bsize,&dfree,&dsize);
200         if((unsigned int)dfree > 0) {
201                 int ret;
202                 if(!recycle_directory_exist(conn,recycle_bin)) {
203                         DEBUG(3, ("recycle bin: directory %s nonexistant, creating...\n", recycle_bin));
204                         if (default_vfs_ops.mkdir(conn,recycle_bin,dir_mask) == -1) {
205                                 DEBUG(3, ("recycle bin: unable to create directory %s. Error was %s\n",
206                                         recycle_bin, strerror(errno) ));
207                         }
208                 }
209                 DEBUG(3, ("recycle bin: move %s -> %s\n", fname, bin));
210
211                 ret = default_vfs_ops.rename(conn, fname, bin);
212                 if (ret == -1) {
213                         DEBUG(3, ("recycle bin: move error %d (%s)\n", errno, strerror(errno) ));
214                         DEBUG(3, ("recycle bin: move failed, purging...\n"));
215                         return default_vfs_ops.unlink(conn,fname);
216                 }
217                 return ret;
218         } else { 
219                 DEBUG(3, ("recycle bin: move failed, purging...\n"));
220                 return default_vfs_ops.unlink(conn,fname);
221         }
222 }