/* * Auditing VFS module for samba. Log selected file operations to syslog * facility. * * Copyright (C) 2001, Brandon Stone, Amherst College, . * Copyright (C) 2002, Jeremy Allison - modified to make a VFS module. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "config.h" #include #include #ifdef HAVE_UTIME_H #include #endif #ifdef HAVE_DIRENT_H #include #endif #include #ifdef HAVE_FCNTL_H #include #endif #include #include #include #include /* VFS operations */ extern struct vfs_ops default_vfs_ops; /* For passthrough operation */ static int recycle_unlink(connection_struct *, const char *); static int recycle_connect(struct connection_struct *conn, const char *service, const char *user); static void recycle_disconnect(struct connection_struct *conn); struct vfs_ops recycle_ops = { /* Disk operations */ recycle_connect, /* connect */ recycle_disconnect, /* disconnect */ NULL, /* disk free */ /* Directory operations */ NULL, /* opendir */ NULL, /* readdir */ NULL, /* mkdir */ NULL, /* rmdir */ NULL, /* closedir */ /* File operations */ NULL, /* open */ NULL, /* close */ NULL, /* read */ NULL, /* write */ NULL, /* lseek */ NULL, /* rename */ NULL, /* fsync */ NULL, /* stat */ NULL, /* fstat */ NULL, /* lstat */ recycle_unlink, NULL, /* chmod */ NULL, /* fchmod */ NULL, /* chown */ NULL, /* fchown */ NULL, /* chdir */ NULL, /* getwd */ NULL, /* utime */ NULL, /* ftruncate */ NULL, /* lock */ NULL, /* symlink */ NULL, /* readlink */ NULL, /* link */ NULL, /* mknod */ NULL, /* realpath */ NULL, /* fget_nt_acl */ NULL, /* get_nt_acl */ NULL, /* fset_nt_acl */ NULL, /* set_nt_acl */ NULL, /* chmod_acl */ NULL, /* fchmod_acl */ NULL, /* sys_acl_get_entry */ NULL, /* sys_acl_get_tag_type */ NULL, /* sys_acl_get_permset */ NULL, /* sys_acl_get_qualifier */ NULL, /* sys_acl_get_file */ NULL, /* sys_acl_get_fd */ NULL, /* sys_acl_clear_perms */ NULL, /* sys_acl_add_perm */ NULL, /* sys_acl_to_text */ NULL, /* sys_acl_init */ NULL, /* sys_acl_create_entry */ NULL, /* sys_acl_set_tag_type */ NULL, /* sys_acl_set_qualifier */ NULL, /* sys_acl_set_permset */ NULL, /* sys_acl_valid */ NULL, /* sys_acl_set_file */ NULL, /* sys_acl_set_fd */ NULL, /* sys_acl_delete_def_file */ NULL, /* sys_acl_get_perm */ NULL, /* sys_acl_free_text */ NULL, /* sys_acl_free_acl */ NULL /* sys_acl_free_qualifier */ }; /* VFS initialisation function. Return initialised vfs_ops structure back to SAMBA. */ struct vfs_ops *vfs_init(int *vfs_version, struct vfs_ops *def_vfs_ops) { struct vfs_ops tmp_ops; *vfs_version = SMB_VFS_INTERFACE_VERSION; memcpy(&tmp_ops, def_vfs_ops, sizeof(struct vfs_ops)); tmp_ops.unlink = recycle_unlink; tmp_ops.connect = recycle_connect; tmp_ops.disconnect = recycle_disconnect; memcpy(&recycle_ops, &tmp_ops, sizeof(struct vfs_ops)); return &recycle_ops; } static int recycle_connect(struct connection_struct *conn, const char *service, const char *user) { pstring opts_str; fstring recycle_bin; char *p; DEBUG(3,("recycle_connect: called for service %s as user %s\n", service, user)); pstrcpy(opts_str, (const char *)lp_vfs_options(SNUM(conn))); if (!*opts_str) { DEBUG(3,("recycle_connect: No options listed (%s).\n", lp_vfs_options(SNUM(conn)) )); return 0; /* No options. */ } p = opts_str; if (next_token(&p,recycle_bin,"=",sizeof(recycle_bin))) { if (!strequal("recycle", recycle_bin)) { DEBUG(3,("recycle_connect: option %s is not recycle\n", recycle_bin )); return -1; } } if (!next_token(&p,recycle_bin," \n",sizeof(recycle_bin))) { DEBUG(3,("recycle_connect: no option after recycle=\n")); return -1; } DEBUG(10,("recycle_connect: recycle name is %s\n", recycle_bin )); conn->vfs_private = (void *)strdup(recycle_bin); return 0; } static void recycle_disconnect(struct connection_struct *conn) { SAFE_FREE(conn->vfs_private); } static BOOL recycle_XXX_exist(connection_struct *conn, const char *dname, BOOL isdir) { SMB_STRUCT_STAT st; if (default_vfs_ops.stat(conn,dname,&st) != 0) return(False); if (isdir) return S_ISDIR(st.st_mode) ? True : False; else return S_ISREG(st.st_mode) ? True : False; } static BOOL recycle_directory_exist(connection_struct *conn, const char *dname) { return recycle_XXX_exist(conn, dname, True); } static BOOL recycle_file_exist(connection_struct *conn, const char *fname) { return recycle_XXX_exist(conn, fname, False); } static SMB_OFF_T recycle_get_file_size(connection_struct *conn, const char *fname) { SMB_STRUCT_STAT st; if (default_vfs_ops.stat(conn,fname,&st) != 0) return (SMB_OFF_T)-1; return(st.st_size); } /******************************************************************** Check if file should be recycled *********************************************************************/ static int recycle_unlink(connection_struct *conn, const char *inname) { fstring recycle_bin; pstring fname; char *base, *ext; pstring bin; int i=1, len, addlen; int dir_mask=0770; SMB_BIG_UINT dfree,dsize,bsize; *recycle_bin = '\0'; pstrcpy(fname, inname); if (conn->vfs_private) fstrcpy(recycle_bin, (const char *)conn->vfs_private); if(!*recycle_bin) { DEBUG(3, ("recycle bin: share parameter not set, purging %s...\n", fname)); return default_vfs_ops.unlink(conn,fname); } if(recycle_get_file_size(conn, fname) == 0) { DEBUG(3, ("recycle bin: file %s is empty, purging...\n", fname)); return default_vfs_ops.unlink(conn,fname); } base = strrchr(fname, '/'); pstrcpy(bin, recycle_bin); pstrcat(bin, "/"); if(base == NULL) { ext = strrchr(fname, '.'); pstrcat(bin, fname); } else { ext = strrchr(base, '.'); pstrcat(bin, base+1); } DEBUG(3, ("recycle bin: base %s, ext %s, fname %s, bin %s\n", base, ext, fname, bin)); if(strcmp(fname,bin) == 0) { DEBUG(3, ("recycle bin: file %s exists, purging...\n", fname)); return default_vfs_ops.unlink(conn,fname); } len = strlen(bin); if ( ext != NULL) len = len - strlen(ext); addlen = sizeof(pstring)-len-1; while(recycle_file_exist(conn,bin)) { slprintf(bin+len, addlen, " (Copy #%d)", i++); pstrcat(bin, ext); } DEBUG(3, ("recycle bin: moving source=%s to dest=%s\n", fname, bin)); default_vfs_ops.disk_free(conn,".",True,&bsize,&dfree,&dsize); if((unsigned int)dfree > 0) { int ret; if(!recycle_directory_exist(conn,recycle_bin)) { DEBUG(3, ("recycle bin: directory %s nonexistant, creating...\n", recycle_bin)); if (default_vfs_ops.mkdir(conn,recycle_bin,dir_mask) == -1) { DEBUG(3, ("recycle bin: unable to create directory %s. Error was %s\n", recycle_bin, strerror(errno) )); } } DEBUG(3, ("recycle bin: move %s -> %s\n", fname, bin)); ret = default_vfs_ops.rename(conn, fname, bin); if (ret == -1) { DEBUG(3, ("recycle bin: move error %d (%s)\n", errno, strerror(errno) )); DEBUG(3, ("recycle bin: move failed, purging...\n")); return default_vfs_ops.unlink(conn,fname); } return ret; } else { DEBUG(3, ("recycle bin: move failed, purging...\n")); return default_vfs_ops.unlink(conn,fname); } }