From 33f44ea7f93b30678659d7e50b23c5056991dfd0 Mon Sep 17 00:00:00 2001 From: Frank Lahm Date: Tue, 24 May 2011 14:24:41 +0200 Subject: [PATCH] Import FCE --- etc/afpd/afp_options.c | 11 + etc/afpd/directory.c | 3 + etc/afpd/fce_api.c | 546 ++++++++++++++++++++++++++++++++++++ etc/afpd/fce_api.h | 28 ++ etc/afpd/fce_api_internal.h | 71 +++++ etc/afpd/fce_util.c | 227 +++++++++++++++ etc/afpd/file.c | 4 + etc/afpd/filedir.c | 18 +- etc/afpd/fork.c | 3 + etc/afpd/fork.h | 1 + etc/afpd/ofork.c | 7 + 11 files changed, 914 insertions(+), 5 deletions(-) create mode 100755 etc/afpd/fce_api.c create mode 100755 etc/afpd/fce_api.h create mode 100755 etc/afpd/fce_api_internal.h create mode 100755 etc/afpd/fce_util.c diff --git a/etc/afpd/afp_options.c b/etc/afpd/afp_options.c index 057a1a32..ff807404 100644 --- a/etc/afpd/afp_options.c +++ b/etc/afpd/afp_options.c @@ -47,6 +47,7 @@ char *strchr (), *strrchr (); #include "globals.h" #include "status.h" #include "auth.h" +#include "fce_api.h" #include @@ -448,6 +449,16 @@ int afp_options_parseline(char *buf, struct afp_options *options) if ((c = getoption(buf, "-ntseparator")) && (opt = strdup(c))) options->ntseparator = opt; + if ((c = getoption(buf, "-fcelistener"))) { + LOG(log_note, logtype_afpd, "Adding fce listener \"%s\"", c); + fce_add_udp_socket(c); + } + if ((c = getoption(buf, "-fcecoalesce"))) { + LOG(log_ote, logtype_afpd, "Fce coalesce: %s", c); + fce_set_coalesce(c); + } + + return 1; } diff --git a/etc/afpd/directory.c b/etc/afpd/directory.c index 0e13a162..4544d28e 100644 --- a/etc/afpd/directory.c +++ b/etc/afpd/directory.c @@ -57,6 +57,7 @@ char *strchr (), *strrchr (); #include "unix.h" #include "mangle.h" #include "hash.h" +#include "fce_api.h" #ifdef HAVE_NFSv4_ACLS extern void addir_inherit_acl(const struct vol *vol); @@ -2530,6 +2531,8 @@ int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_ ad_setname(&ad, s_path->m_name); ad_setid( &ad, s_path->st.st_dev, s_path->st.st_ino, dir->d_did, did, vol->v_stamp); + fce_register_new_dir(s_path); + ad_flush( &ad); ad_close_metadata( &ad); diff --git a/etc/afpd/fce_api.c b/etc/afpd/fce_api.c new file mode 100755 index 00000000..6443bf30 --- /dev/null +++ b/etc/afpd/fce_api.c @@ -0,0 +1,546 @@ +/* + * $Id: fce_api.c,v 0.01 2010-10-01 00:00:0 mw Exp $ + * + * Copyright (c) 2010 Mark Williams + * + * File change event API for netatalk + * + * for every detected filesystem change a UDP packet is sent to an arbitrary list + * of listeners. Each packet contains unix path of modified filesystem element, + * event reason, and a consecutive event id (32 bit). Technically we are UDP client and are sending + * out packets synchronuosly as they are created by the afp functions. This should not affect + * performance measurably. The only delaying calls occur during initialization, if we have to + * resolve non-IP hostnames to IP. All numeric data inside the packet is network byte order, so use + * ntohs / ntohl to resolve length and event id. Ideally a listener receives every packet with + * no gaps in event ids, starting with event id 1 and mode FCE_CONN_START followed by + * data events from id 2 up to 0xFFFFFFFF, followed by 0 to 0xFFFFFFFF and so on. + * + * A gap or not starting with 1 mode FCE_CONN_START or receiving mode FCE_CONN_BROKEN means that + * the listener has lost at least one filesystem event + * + * All Rights Reserved. See COPYRIGHT. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include + +#include +#include +#include +#include + + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "fork.h" +#include "file.h" +#include "globals.h" +#include "directory.h" +#include "desktop.h" +#include "volume.h" + +#include "fce_api.h" + +// ONLY USED IN THIS FILE +#include "fce_api_internal.h" + +#define FCE_TRUE 1 +#define FCE_FALSE 0 + +/* We store our connection data here */ +static struct udp_entry udp_socket_list[FCE_MAX_UDP_SOCKS]; +static int udp_sockets = 0; +static int udp_initialized = FCE_FALSE; + + +static const char *skip_files[] = +{ + ".DS_Store", + NULL +}; + +/* + * + * Initialize network structs for any listeners + * We dont give return code because all errors are handled internally (I hope..) + * + * */ +void fce_init_udp() +{ + if (udp_initialized == FCE_TRUE) + return; + + + for (int i = 0; i < udp_sockets; i++) + { + struct udp_entry *udp_entry = udp_socket_list + i; + + /* Close any pending sockets */ + if (udp_entry->sock != -1) + { + close( udp_entry->sock ); + } + + /* resolve IP to network address */ + if (inet_aton( udp_entry->ip, &udp_entry->addr.sin_addr ) ==0 ) + { + /* Hmm, failed try to resolve host */ + struct hostent *hp = gethostbyname( udp_entry->ip ); + if (hp == NULL) + { + LOG(log_error, logtype_afpd, "Cannot resolve host name for fce UDP connection: %s (errno %d)", udp_entry->ip, errno ); + continue; + } + memcpy( &udp_entry->addr.sin_addr, &hp->h_addr, sizeof(udp_entry->addr.sin_addr) ); + } + + /* Create UDP socket */ + udp_entry->sock = socket( AF_INET, SOCK_DGRAM, 0 ); + if (udp_entry->sock == -1) + { + LOG(log_error, logtype_afpd, "Cannot create socket for fce UDP connection: errno %d", errno ); + continue; + } + + /* Set socket address params */ + udp_entry->addr.sin_family = AF_INET; + udp_entry->addr.sin_port = htons(udp_entry->port); + } + udp_initialized = FCE_TRUE; + +} +void fce_cleanup() +{ + if (udp_initialized == FCE_FALSE ) + return; + + for (int i = 0; i < udp_sockets; i++) + { + struct udp_entry *udp_entry = udp_socket_list + i; + + /* Close any pending sockets */ + if (udp_entry->sock != -1) + { + close( udp_entry->sock ); + udp_entry->sock = -1; + } + } + udp_initialized = FCE_FALSE; +} + + +/* + * Construct a UDP packet for our listeners and return packet size + * */ +static unsigned short build_fce_packet( struct fce_packet *packet, char *path, int mode, uint32_t event_id ) +{ + unsigned short data_len = 0; + + strncpy(packet->magic, FCE_PACKET_MAGIC, sizeof(packet->magic) ); + packet->version = FCE_PACKET_VERSION; + packet->mode = mode; + + data_len = strlen( path ); + + /* This should never happen, but before we bust this server, we send nonsense, fce listener has to cope */ + if (data_len >= FCE_MAX_PATH_LEN) + { + data_len = FCE_MAX_PATH_LEN - 1; + } + + /* This is the payload len. Means: the stream has len bytes more until packet is finished */ + /* A server should read the first 16 byte, decode them and then fetch the rest */ + packet->len = htons( data_len); + packet->event_id = htonl( event_id ); + + strncpy( packet->data, path, data_len ); + + /* return the packet len */ + return sizeof(struct fce_packet) - FCE_MAX_PATH_LEN + data_len; +} + +/* + * Send the fce information to all (connected) listeners + * We dont give return code because all errors are handled internally (I hope..) + * */ +static void send_fce_event( char *path, int mode ) +{ + struct fce_packet packet; + void *data = &packet; + static uint32_t event_id = 0; /* the unique packet couter to detect packet/data loss. Going from 0xFFFFFFFF to 0x0 is a valid increment */ + + time_t now = time(NULL); + + /* build our data packet */ + int data_len = build_fce_packet( &packet, path, mode, ++event_id ); + + + for (int i = 0; i < udp_sockets; i++) + { + int sent_data = 0; + struct udp_entry *udp_entry = udp_socket_list + i; + + /* we had a problem earlier ? */ + if (udp_entry->sock == -1) + { + /* We still have to wait ?*/ + if (now < udp_entry->next_try_on_error) + continue; + + /* Reopen socket */ + udp_entry->sock = socket( AF_INET, SOCK_DGRAM, 0 ); + + if (udp_entry->sock == -1) + { + /* failed again, so go to rest again */ + LOG(log_error, logtype_afpd, "Cannot recreate socket for fce UDP connection: errno %d", errno ); + + udp_entry->next_try_on_error = now + FCE_SOCKET_RETRY_DELAY_S; + continue; + } + + udp_entry->next_try_on_error = 0; + + /* Okay, we have a running socket again, send server that we had a problem on our side*/ + data_len = build_fce_packet( &packet, "", FCE_CONN_BROKEN, 0 ); + + sendto( udp_entry->sock, data, data_len, 0, &udp_entry->addr, sizeof(udp_entry->addr) ); + + /* Rebuild our original data packet */ + data_len = build_fce_packet( &packet, path, mode, event_id ); + } + + sent_data = sendto( udp_entry->sock, data, data_len, 0, &udp_entry->addr, sizeof(udp_entry->addr) ); + + /* Problems ? */ + if (sent_data != data_len) + { + /* Argh, socket broke, we close and retry later */ + LOG(log_error, logtype_afpd, "Error while sending packet to %s for fce UDP connection: transfered: %d of %d errno %d", + udp_entry->port, sent_data, data_len, errno ); + + close( udp_entry->sock ); + udp_entry->sock = -1; + udp_entry->next_try_on_error = now + FCE_SOCKET_RETRY_DELAY_S; + } + } +} + +static int add_udp_socket( char *target_ip, int target_port ) +{ + if (target_port == 0) + target_port = FCE_DEFAULT_PORT; + + if (udp_sockets >= FCE_MAX_UDP_SOCKS) + { + LOG(log_error, logtype_afpd, "Too many file change api UDP connections (max %d allowed)", FCE_MAX_UDP_SOCKS ); + return AFPERR_PARAM; + } + + strncpy( udp_socket_list[udp_sockets].ip, target_ip, FCE_MAX_IP_LEN - 1); + udp_socket_list[udp_sockets].port = target_port; + udp_socket_list[udp_sockets].sock = -1; + memset( &udp_socket_list[udp_sockets].addr, 0, sizeof(struct sockaddr_in) ); + udp_socket_list[udp_sockets].next_try_on_error = 0; + + udp_sockets++; + + return AFP_OK; +} + +/* + * + * Dispatcher for all incoming file change events + * + * */ +static int register_fce( char *u_name, int is_dir, int mode ) +{ + if (u_name == NULL) + return AFPERR_PARAM; + + static int first_event = FCE_TRUE; + + /* do some initialization on the fly the first time */ + if (first_event) + { + fce_initialize_history(); + } + + + /* handle files which should not cause events (.DS_Store atc. ) */ + for (int i = 0; skip_files[i] != NULL; i++) + { + if (!strcmp( u_name, skip_files[i])) + return AFP_OK; + } + + + char full_path_buffer[FCE_MAX_PATH_LEN + 1] = {""}; + const char *cwd = getcwdpath(); + + if (!is_dir || mode == FCE_DIR_DELETE) + { + if (strlen( cwd ) + strlen( u_name) + 1 >= FCE_MAX_PATH_LEN) + { + LOG(log_error, logtype_afpd, "FCE file name too long: %s/%s", cwd, u_name ); + return AFPERR_PARAM; + } + sprintf( full_path_buffer, "%s/%s", cwd, u_name ); + } + else + { + if (strlen( cwd ) >= FCE_MAX_PATH_LEN) + { + LOG(log_error, logtype_afpd, "FCE directory name too long: %s", cwd); + return AFPERR_PARAM; + } + strcpy( full_path_buffer, cwd); + } + + /* Can we ignore this event based on type or history? */ + if (fce_handle_coalescation( full_path_buffer, is_dir, mode )) + { + LOG(log_debug9, logtype_afpd, "Coalesced fc event <%d> for <%s>", mode, full_path_buffer ); + return AFP_OK; + } + + LOG(log_debug9, logtype_afpd, "Detected fc event <%d> for <%s>", mode, full_path_buffer ); + + + /* we do initilization on the fly, no blocking calls in here + * (except when using FQDN in broken DNS environment) + */ + if (first_event == FCE_TRUE) + { + fce_init_udp(); + + /* Notify listeners the we start from the beginning */ + send_fce_event( "", FCE_CONN_START ); + + first_event = FCE_FALSE; + } + + /* Handle UDP transport */ + send_fce_event( full_path_buffer, mode ); + + return AFP_OK; +} + + +/******************** External calls start here **************************/ + +/* + * API-Calls for file change api, called form outside (file.c directory.c ofork.c filedir.c) + * */ +#ifndef FCE_TEST_MAIN + + +int fce_register_delete_file( struct path *path ) +{ + int ret = AFP_OK; + + if (path == NULL) + return AFPERR_PARAM; + + + ret = register_fce( path->u_name, FALSE, FCE_FILE_DELETE ); + + return ret; +} +int fce_register_delete_dir( char *name ) +{ + int ret = AFP_OK; + + if (name == NULL) + return AFPERR_PARAM; + + + ret = register_fce( name, TRUE, FCE_DIR_DELETE); + + return ret; +} + +int fce_register_new_dir( struct path *path ) +{ + int ret = AFP_OK; + + if (path == NULL) + return AFPERR_PARAM; + + ret = register_fce( path->u_name, TRUE, FCE_DIR_CREATE ); + + return ret; +} + + +int fce_register_new_file( struct path *path ) +{ + int ret = AFP_OK; + + if (path == NULL) + return AFPERR_PARAM; + + ret = register_fce( path->u_name, FALSE, FCE_FILE_CREATE ); + + return ret; +} + + +int fce_register_file_modification( struct ofork *ofork ) +{ + char *u_name = NULL; + struct dir *dir; + struct vol *vol; + int ret = AFP_OK; + + if (ofork == NULL || ofork->of_vol == NULL || ofork->of_dir == NULL) + return AFPERR_PARAM; + + vol = ofork->of_vol; + dir = ofork->of_dir; + + if (NULL == (u_name = mtoupath(vol, of_name(ofork), dir->d_did, utf8_encoding()))) + { + return AFPERR_MISC; + } + + ret = register_fce( u_name, FALSE, FCE_FILE_MODIFY ); + + return ret; +} +#endif + +/* + * + * Extern connect to afpd parameter, can be called multiple times for multiple listeners (up to MAX_UDP_SOCKS times) + * + * */ +int fce_add_udp_socket( char *target ) +{ + int port = FCE_DEFAULT_PORT; + char target_ip[256] = {""}; + + strncpy( target_ip, target, sizeof(target_ip) -1); + char *port_delim = strchr( target_ip, ':' ); + if (port_delim) + { + *port_delim = 0; + port = atoi( port_delim + 1); + } + return add_udp_socket( target_ip, port ); +} + + + +#ifdef FCE_TEST_MAIN + + +void shortsleep( unsigned int us ) +{ + usleep( us ); +} +int main( int argc, char*argv[] ) +{ + int port = 11250; + char *host = NULL; + int delay_between_events = 1000; + int event_code = FCE_FILE_MODIFY; + char pathbuff[1024]; + int duration_in_seconds = 0; // TILL ETERNITY + + char *path = getcwd( pathbuff, sizeof(pathbuff) ); + + // FULLSPEED TEST IS "-s 1001" -> delay is 0 -> send packets without pause + + if (argc == 1) + { + fprintf( stdout, "%s: -p Port -h Listener1 [ -h Listener2 ...] -P path -s Delay_between_events_in_us -e event_code -d Duration \n", argv[0]); + exit( 1 ); + } + int ret = AFP_OK; + + for (int i = 1; i < argc; i++) + { + char *p = argv[i]; + if (*p == '-' && p[1]) + { + char *arg = argv[i + 1]; + switch (p[1]) + { + case 'p': if (arg) port = atoi( arg ), i++; break; + case 'P': if (arg) path = arg, i++; break; + case 's': if (arg) delay_between_events = atoi( arg ), i++; break; + case 'e': if (arg) event_code = atoi( arg ), i++; break; + case 'd': if (arg) duration_in_seconds = atoi( arg ), i++; break; + case 'h': + { + if (arg) + { + host = arg; + char target[256]; + sprintf( target, "%s:%d", host, port ); + ret += fce_add_udp_socket( target ); + i++; + } + break; + } + } + } + } + + + if (host == NULL) + { + char target[256]; + sprintf( target, "127.0.0.1:%d", port ); + ret += fce_add_udp_socket( target ); + } + + if (ret) + return ret; + + + int ev_cnt = 0; + time_t start_time = time(NULL); + time_t end_time = 0; + + if (duration_in_seconds) + end_time = start_time + duration_in_seconds; + + while (1) + { + time_t now = time(NULL); + if (now > start_time) + { + start_time = now; + fprintf( stdout, "%d events/s\n", ev_cnt ); + ev_cnt = 0; + } + if (end_time && now >= end_time) + break; + + register_fce( path, event_code ); + ev_cnt++; + + + shortsleep( delay_between_events ); + } +} +#endif /* TESTMAIN*/ diff --git a/etc/afpd/fce_api.h b/etc/afpd/fce_api.h new file mode 100755 index 00000000..9d896f64 --- /dev/null +++ b/etc/afpd/fce_api.h @@ -0,0 +1,28 @@ +/* + * File: fce_api.h + * Author: mw + * + * Created on 1. Oktober 2010, 21:35 + * + * API calls for file change event api + */ + +#ifndef _FCE_API_H +#define _FCE_API_H + +struct path; +struct ofork; +int fce_register_delete_file( struct path *path ); +int fce_register_delete_dir( char *name ); +int fce_register_new_dir( struct path *path ); +int fce_register_new_file( struct path *path ); +int fce_register_file_modification( struct ofork *ofork ); + +int fce_add_udp_socket( char *target ); // IP or IP:Port +int fce_set_coalesce( char *coalesce_opt ); // all|delete|create + +#define FCE_DEFAULT_PORT 12250 + + +#endif /* _FCE_API_H */ + diff --git a/etc/afpd/fce_api_internal.h b/etc/afpd/fce_api_internal.h new file mode 100755 index 00000000..959ce9ea --- /dev/null +++ b/etc/afpd/fce_api_internal.h @@ -0,0 +1,71 @@ +/* + * File: fce_api_internal.h + * Author: mw + * + * Created on 1. Oktober 2010, 23:48 + */ + +#ifndef _FCE_API_INTERNAL_H +#define _FCE_API_INTERNAL_H + +#define FCE_FILE_MODIFY 1 +#define FCE_FILE_DELETE 2 +#define FCE_DIR_DELETE 3 +#define FCE_FILE_CREATE 4 +#define FCE_DIR_CREATE 5 +#define FCE_CONN_START 42 +#define FCE_CONN_BROKEN 99 + + +#define FCE_MAX_PATH_LEN 1024 + +#define FCE_MAX_UDP_SOCKS 5 /* Allow a maximum of udp listeners for file change events */ +#define FCE_MAX_IP_LEN 255 /* Man len of listener name */ +#define FCE_SOCKET_RETRY_DELAY_S 600 /* Pause this time in s after socket was broken */ +#define FCE_PACKET_VERSION 1 + + +#define FCE_PACKET_MAGIC "at_fcapi" /* Must fit to size of fce_packet.fce_magic */ + +#define FCE_HISTORY_LEN 10 /* This is used to coalesce events */ +#define MAX_COALESCE_TIME_MS 1000 /* Events oldeer than this are not coalesced */ + +struct udp_entry +{ + int sock; + char ip[FCE_MAX_IP_LEN]; + int port; + struct sockaddr_in addr; + time_t next_try_on_error; /* In case of error set next timestamp to retry */ +}; + +/* This packet goes over the network, so we want to + * be shure about datastructs and type sizes between platforms + */ +struct fce_packet +{ + char magic[8]; + unsigned char version; + unsigned char mode; + uint16_t len; /* network byte order */ + uint32_t event_id; /* network byte order */ + char data[FCE_MAX_PATH_LEN]; +}; + +struct fce_history +{ + unsigned char mode; + int is_dir; + char path[FCE_MAX_PATH_LEN + 1]; + struct timeval tv; +}; + + +#define PACKET_HDR_LEN (sizeof(struct fce_packet) - FCE_MAX_PATH_LEN) + +int fce_handle_coalescation( char *path, int is_dir, int mode ); +void fce_initialize_history(); + + +#endif /* _FCE_API_INTERNAL_H */ + diff --git a/etc/afpd/fce_util.c b/etc/afpd/fce_util.c new file mode 100755 index 00000000..8a89e244 --- /dev/null +++ b/etc/afpd/fce_util.c @@ -0,0 +1,227 @@ +/* + * $Id: fce_api.c,v 0.01 2010-10-01 00:00:0 mw Exp $ + * + * Copyright (c) 2010 Mark Williams + * + * File change event API for netatalk + * + * for every detected filesystem change a UDP packet is sent to an arbitrary list + * of listeners. Each packet contains unix path of modified filesystem element, + * event reason, and a consecutive event id (32 bit). Technically we are UDP client and are sending + * out packets synchronuosly as they are created by the afp functions. This should not affect + * performance measurably. The only delaying calls occur during initialization, if we have to + * resolve non-IP hostnames to IP. All numeric data inside the packet is network byte order, so use + * ntohs / ntohl to resolve length and event id. Ideally a listener receives every packet with + * no gaps in event ids, starting with event id 1 and mode FCE_CONN_START followed by + * data events from id 2 up to 0xFFFFFFFF, followed by 0 to 0xFFFFFFFF and so on. + * + * A gap or not starting with 1 mode FCE_CONN_START or receiving mode FCE_CONN_BROKEN means that + * the listener has lost at least one filesystem event + * + * All Rights Reserved. See COPYRIGHT. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include + +#include +#include +#include +#include + + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "fork.h" +#include "file.h" +#include "globals.h" +#include "directory.h" +#include "desktop.h" +#include "volume.h" + +#include "fce_api.h" + +// ONLY USED IN THIS FILE +#include "fce_api_internal.h" + +#define FCE_TRUE 1 +#define FCE_FALSE 0 + +/* We store our connection data here */ +static char coalesce[80] = {""}; +static struct fce_history fce_history_list[FCE_HISTORY_LEN]; + + + + +/**** +* With coalesce we try to reduce the events over UDP, the eventlistener would throw these +* events away anyway. +* This works only, if the connected listener uses the events on a "per directory" base +* It is a very simple aproach, but saves a lot of events sent to listeners. +* Every "child element" event is ignored as long as its parent event is not older +* than MAX_COALESCE_TIME_MS ms. If large directory trees or large files are created or deleted, +* this probably will not work recursive, because the time to copy data will exceed this +* event timeout. +* +****/ +static int coalesce_none() +{ + return coalesce[0] == 0; +} +static int coalesce_all() +{ + return !strcmp( coalesce, "all" ); +} +static int coalesce_create() +{ + return !strcmp( coalesce, "create" ) || coalesce_all(); +} +static int coalesce_delete() +{ + return !strcmp( coalesce, "delete" ) || coalesce_all(); +} + +void fce_initialize_history() +{ + for (int i = 0; i < FCE_HISTORY_LEN; i++) + { + memset( &fce_history_list[i], 0, sizeof(fce_history_list[i]) ); + } +} + +static long get_ms_difftime ( struct timeval *tv1, struct timeval *tv2 ) +{ + unsigned long s = tv2->tv_sec - tv1->tv_sec; + long us = tv2->tv_usec - tv1->tv_usec; + + return s * 1000 + us/1000; +} + +int fce_handle_coalescation( char *path, int is_dir, int mode ) +{ + if (coalesce_none()) + return FALSE; + + + + // First one: + // After a file creation *ALWAYS* a file modification is produced + if (mode == FCE_FILE_CREATE) + { + if (coalesce_create()) + { + return TRUE; + } + } + + /* get timestamp */ + struct timeval tv; + gettimeofday(&tv, 0); + + + /* These two are used to eval our next index in history */ + /* the history is unsorted, speed should not be a problem, length is 10 */ + unsigned long oldest_entry = (unsigned long )((long)-1); + int oldest_entry_idx = -1; + + /* Now detect events in the very near history */ + for (int i = 0; i < FCE_HISTORY_LEN; i++) + { + struct fce_history *fh = &fce_history_list[i]; + + //* Not inited ? */ + if (fh->tv.tv_sec == 0) + { + /* we can use it for new elements */ + oldest_entry = 0; + oldest_entry_idx = i; + continue; + } + + //* Too old ? */ + if (get_ms_difftime( &fh->tv, &tv ) > MAX_COALESCE_TIME_MS) + { + /* Invalidate entry */ + fh->tv.tv_sec = 0; + + oldest_entry = 0; + oldest_entry_idx = i; + continue; + } + + + /* If we find a parent dir wich was created we are done */ + if (coalesce_create() && fh->mode == FCE_DIR_CREATE) + { + //* Parent dir ? */ + if (!strncmp( fh->path, path, strlen( fh->path ) ) ) + { + return TRUE; + } + } + + /* If we find a parent dir we should be DELETED we are done */ + if (coalesce_delete() && fh->is_dir && (mode == FCE_FILE_DELETE || mode == FCE_DIR_DELETE)) + { + //* Parent dir ? */ + if (!strncmp( fh->path, path, strlen( fh->path ) ) ) + { + return TRUE; + } + } + + //* Detect oldest entry for next new entry */ + if (oldest_entry_idx == -1 || fh->tv.tv_sec < oldest_entry) + { + oldest_entry = fh->tv.tv_sec; + oldest_entry_idx = i; + } + } + + /* We have a new entry for the history, register it */ + fce_history_list[oldest_entry_idx].tv = tv; + fce_history_list[oldest_entry_idx].mode = mode; + fce_history_list[oldest_entry_idx].is_dir = is_dir; + strncpy( fce_history_list[oldest_entry_idx].path, path, FCE_MAX_PATH_LEN ); + + /* we have to handle this event */ + return FALSE; + +} + + + +/* + * + * Set event coalescation to reduce number of events sent over UDP + * all|delete|create + * + * + * */ + +int fce_set_coalesce( char *coalesce_opt ) +{ + strncpy( coalesce, coalesce_opt, sizeof(coalesce) - 1 ); +} + + + + diff --git a/etc/afpd/file.c b/etc/afpd/file.c index b10330c8..4aea84dd 100644 --- a/etc/afpd/file.c +++ b/etc/afpd/file.c @@ -48,6 +48,7 @@ char *strchr (), *strrchr (); #include "filedir.h" #include "globals.h" #include "unix.h" +#include "fce_api.h" /* the format for the finderinfo fields (from IM: Toolbox Essentials): * field bytes subfield bytes @@ -704,6 +705,9 @@ int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, path = s_path->m_name; ad_setname(adp, path); ad_flush( adp); + + fce_register_new_file(s_path); + ad_close( adp, ADFLAGS_DF|ADFLAGS_HF ); createfile_done: diff --git a/etc/afpd/filedir.c b/etc/afpd/filedir.c index 3e5cb2f5..46958696 100644 --- a/etc/afpd/filedir.c +++ b/etc/afpd/filedir.c @@ -48,6 +48,7 @@ char *strchr (), *strrchr (); #include "globals.h" #include "filedir.h" #include "unix.h" +#include "fce_api.h" #ifdef DROPKLUDGE int matchfile2dirperms( @@ -562,8 +563,15 @@ int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size if (*s_path->m_name != '\0') { rc = AFPERR_ACCESS; } - else { - rc = deletecurdir( vol); + else + { + + /* we have to cache this, the structs are lost in deletcurdir*/ + /* but we need the positive returncode to send our event */ + char dname[256]; + strncpy(dname, curdir->d_u_name, 255 ); + if ((rc = deletecurdir(vol)) == AFP_OK) + fce_register_delete_dir(dname); } } else if (of_findname(s_path)) { rc = AFPERR_BUSY; @@ -574,9 +582,9 @@ int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size */ if (s_path->st_valid && s_path->st_errno == ENOENT) { rc = AFPERR_NOOBJ; - } - else { - rc = deletefile(vol, -1, upath, 1); + } else { + if ((rc = deletefile(vol, -1, upath, 1)) === AFP_OK) + fce_register_delete_file( s_path ); } } if ( rc == AFP_OK ) { diff --git a/etc/afpd/fork.c b/etc/afpd/fork.c index b9d03507..78d965ee 100644 --- a/etc/afpd/fork.c +++ b/etc/afpd/fork.c @@ -1336,6 +1336,9 @@ static int write_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, s if ( ad_meta_fileno( ofork->of_ad ) != -1 ) /* META */ ofork->of_flags |= AFPFORK_DIRTY; + /* we have modified any fork, remember until close_fork */ + ofork->of_flags |= AFPFORK_MODIFIED; + *rbuflen = set_off_t (offset, rbuf, is64); return( AFP_OK ); diff --git a/etc/afpd/fork.h b/etc/afpd/fork.h index 5a42afc7..c4e25de1 100644 --- a/etc/afpd/fork.h +++ b/etc/afpd/fork.h @@ -50,6 +50,7 @@ struct ofork { #define AFPFORK_ACCRD (1<<4) #define AFPFORK_ACCWR (1<<5) #define AFPFORK_ACCMASK (AFPFORK_ACCRD | AFPFORK_ACCWR) +#define AFPFORK_MODIFIED (1<<6) /* used in FCE for modified files */ #ifdef AFS extern struct ofork *writtenfork; diff --git a/etc/afpd/ofork.c b/etc/afpd/ofork.c index 49c014cc..5d3c48bc 100644 --- a/etc/afpd/ofork.c +++ b/etc/afpd/ofork.c @@ -26,6 +26,7 @@ #include "volume.h" #include "directory.h" #include "fork.h" +#include "fce_api.h" /* we need to have a hashed list of oforks (by dev inode). just hash * by first letter. */ @@ -454,6 +455,12 @@ int of_closefork(struct ofork *ofork) } } } + + /* Somone has used write_fork, we assume file was changed, register it to file change event api */ + if (ofork->of_flags & AFPFORK_MODIFIED) { + fce_register_file_modification(ofork); + } + ret = 0; if ( ad_close( ofork->of_ad, adflags ) < 0 ) { ret = -1; -- 2.39.2