change.c 30.6 KB
Newer Older
1 2 3 4
/*
 * Server-side change notification management
 *
 * Copyright (C) 1998 Alexandre Julliard
5
 * Copyright (C) 2006 Mike McCormack
6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 21
 */

22 23 24
#include "config.h"
#include "wine/port.h"

25
#include <assert.h>
26
#include <fcntl.h>
27 28
#include <stdio.h>
#include <stdlib.h>
29
#include <signal.h>
30
#include <sys/stat.h>
31 32 33
#include <sys/types.h>
#include <limits.h>
#include <dirent.h>
34
#include <errno.h>
35

36 37
#include "ntstatus.h"
#define WIN32_NO_STATUS
38
#include "windef.h"
39

40
#include "file.h"
41 42
#include "handle.h"
#include "thread.h"
43
#include "request.h"
44 45
#include "process.h"
#include "security.h"
46
#include "winternl.h"
47

48 49
/* dnotify support */

50 51 52 53 54 55 56 57
#ifdef linux
#ifndef F_NOTIFY
#define F_NOTIFY 1026
#define DN_ACCESS       0x00000001      /* File accessed */
#define DN_MODIFY       0x00000002      /* File modified */
#define DN_CREATE       0x00000004      /* File created */
#define DN_DELETE       0x00000008      /* File removed */
#define DN_RENAME       0x00000010      /* File renamed */
58
#define DN_ATTRIB       0x00000020      /* File changed attributes */
59 60 61 62
#define DN_MULTISHOT    0x80000000      /* Don't remove notifier */
#endif
#endif

63 64
/* inotify support */

65 66 67 68
#ifdef HAVE_SYS_INOTIFY_H
#include <sys/inotify.h>
#define USE_INOTIFY
#elif defined(__linux__) && defined(__i386__)
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93

#define SYS_inotify_init	291
#define SYS_inotify_add_watch	292
#define SYS_inotify_rm_watch	293

struct inotify_event {
    int           wd;
    unsigned int  mask;
    unsigned int  cookie;
    unsigned int  len;
    char          name[1];
};

#define IN_ACCESS        0x00000001
#define IN_MODIFY        0x00000002
#define IN_ATTRIB        0x00000004
#define IN_CLOSE_WRITE   0x00000008
#define IN_CLOSE_NOWRITE 0x00000010
#define IN_OPEN          0x00000020
#define IN_MOVED_FROM    0x00000040
#define IN_MOVED_TO      0x00000080
#define IN_CREATE        0x00000100
#define IN_DELETE        0x00000200
#define IN_DELETE_SELF   0x00000400

94 95
#define IN_ISDIR         0x40000000

96 97
static inline int inotify_init( void )
{
98
    return syscall( SYS_inotify_init );
99 100 101 102
}

static inline int inotify_add_watch( int fd, const char *name, unsigned int mask )
{
103
    return syscall( SYS_inotify_add_watch, fd, name, mask );
104 105
}

106
static inline int inotify_rm_watch( int fd, int wd )
107
{
108
    return syscall( SYS_inotify_rm_watch, fd, wd );
109 110
}

111
#define USE_INOTIFY
112 113 114

#endif

115 116 117 118 119 120
struct inode;

static void free_inode( struct inode *inode );

static struct fd *inotify_fd;

121 122
struct change_record {
    struct list entry;
123
    unsigned int cookie;
124
    struct filesystem_event event;
125 126
};

127
struct dir
128 129
{
    struct object  obj;      /* object header */
130
    struct fd     *fd;       /* file descriptor to the directory */
131 132
    mode_t         mode;     /* file stat.st_mode */
    uid_t          uid;      /* file stat.st_uid */
133 134
    struct list    entry;    /* entry in global change notifications list */
    unsigned int   filter;   /* notification filter */
135
    int            notified; /* SIGIO counter */
136
    int            want_data; /* return change data */
137
    int            subtree;  /* do we want to watch subdirectories? */
138
    struct list    change_records;   /* data for the change */
139 140
    struct list    in_entry; /* entry in the inode dirs list */
    struct inode  *inode;    /* inode of the associated directory */
141 142
};

143
static struct fd *dir_get_fd( struct object *obj );
144 145 146
static struct security_descriptor *dir_get_sd( struct object *obj );
static int dir_set_sd( struct object *obj, const struct security_descriptor *sd,
                       unsigned int set_info );
147 148
static void dir_dump( struct object *obj, int verbose );
static void dir_destroy( struct object *obj );
149

150
static const struct object_ops dir_ops =
151
{
152 153
    sizeof(struct dir),       /* size */
    dir_dump,                 /* dump */
154
    no_get_type,              /* get_type */
155 156
    add_queue,                /* add_queue */
    remove_queue,             /* remove_queue */
157
    default_fd_signaled,      /* signaled */
158
    no_satisfied,             /* satisfied */
159
    no_signal,                /* signal */
160
    dir_get_fd,               /* get_fd */
161
    default_fd_map_access,    /* map_access */
162 163
    dir_get_sd,               /* get_sd */
    dir_set_sd,               /* set_sd */
164
    no_lookup_name,           /* lookup_name */
165
    no_open_file,             /* open_file */
166
    fd_close_handle,          /* close_handle */
167 168 169 170
    dir_destroy               /* destroy */
};

static int dir_get_poll_events( struct fd *fd );
171
static enum server_fd_type dir_get_fd_type( struct fd *fd );
172 173 174

static const struct fd_ops dir_fd_ops =
{
175 176 177
    dir_get_poll_events,         /* get_poll_events */
    default_poll_event,          /* poll_event */
    no_flush,                    /* flush */
178
    dir_get_fd_type,             /* get_fd_type */
179
    default_fd_ioctl,            /* ioctl */
180 181 182
    default_fd_queue_async,      /* queue_async */
    default_fd_reselect_async,   /* reselect_async */
    default_fd_cancel_async      /* cancel_async */
183 184
};

185 186
static struct list change_list = LIST_INIT(change_list);

187
static void dnotify_adjust_changes( struct dir *dir )
188
{
189
#if defined(F_SETSIG) && defined(F_NOTIFY)
190 191
    int fd = get_unix_fd( dir->fd );
    unsigned int filter = dir->filter;
192 193 194 195 196
    unsigned int val;
    if ( 0 > fcntl( fd, F_SETSIG, SIGIO) )
        return;

    val = DN_MULTISHOT;
197
    if (filter & FILE_NOTIFY_CHANGE_FILE_NAME)
198
        val |= DN_RENAME | DN_DELETE | DN_CREATE;
199
    if (filter & FILE_NOTIFY_CHANGE_DIR_NAME)
200
        val |= DN_RENAME | DN_DELETE | DN_CREATE;
201
    if (filter & FILE_NOTIFY_CHANGE_ATTRIBUTES)
202
        val |= DN_ATTRIB;
203
    if (filter & FILE_NOTIFY_CHANGE_SIZE)
204
        val |= DN_MODIFY;
205
    if (filter & FILE_NOTIFY_CHANGE_LAST_WRITE)
206
        val |= DN_MODIFY;
207
    if (filter & FILE_NOTIFY_CHANGE_LAST_ACCESS)
208
        val |= DN_ACCESS;
209
    if (filter & FILE_NOTIFY_CHANGE_CREATION)
210
        val |= DN_CREATE;
211
    if (filter & FILE_NOTIFY_CHANGE_SECURITY)
212 213 214 215 216 217
        val |= DN_ATTRIB;
    fcntl( fd, F_NOTIFY, val );
#endif
}

/* insert change in the global list */
218
static inline void insert_change( struct dir *dir )
219 220 221 222 223 224
{
    sigset_t sigset;

    sigemptyset( &sigset );
    sigaddset( &sigset, SIGIO );
    sigprocmask( SIG_BLOCK, &sigset, NULL );
225
    list_add_head( &change_list, &dir->entry );
226 227 228 229
    sigprocmask( SIG_UNBLOCK, &sigset, NULL );
}

/* remove change from the global list */
230
static inline void remove_change( struct dir *dir )
231 232 233 234 235 236
{
    sigset_t sigset;

    sigemptyset( &sigset );
    sigaddset( &sigset, SIGIO );
    sigprocmask( SIG_BLOCK, &sigset, NULL );
237
    list_remove( &dir->entry );
238 239
    sigprocmask( SIG_UNBLOCK, &sigset, NULL );
}
240

241
static void dir_dump( struct object *obj, int verbose )
242
{
243 244
    struct dir *dir = (struct dir *)obj;
    assert( obj->ops == &dir_ops );
245
    fprintf( stderr, "Dirfile fd=%p filter=%08x\n", dir->fd, dir->filter );
246 247 248 249 250
}

/* enter here directly from SIGIO signal handler */
void do_change_notify( int unix_fd )
{
251
    struct dir *dir;
252 253

    /* FIXME: this is O(n) ... probably can be improved */
254
    LIST_FOR_EACH_ENTRY( dir, &change_list, struct dir, entry )
255
    {
256 257
        if (get_unix_fd( dir->fd ) != unix_fd) continue;
        interlocked_xchg_add( &dir->notified, 1 );
258 259 260 261 262 263 264
        break;
    }
}

/* SIGIO callback, called synchronously with the poll loop */
void sigio_callback(void)
{
265
    struct dir *dir;
266

267
    LIST_FOR_EACH_ENTRY( dir, &change_list, struct dir, entry )
268
    {
269 270
        if (interlocked_xchg( &dir->notified, 0 ))
            fd_async_wake_up( dir->fd, ASYNC_TYPE_WAIT, STATUS_NOTIFY_ENUM_DIR );
271
    }
272 273
}

274
static struct fd *dir_get_fd( struct object *obj )
275
{
276 277 278 279
    struct dir *dir = (struct dir *)obj;
    assert( obj->ops == &dir_ops );
    return (struct fd *)grab_object( dir->fd );
}
280

281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
static int get_dir_unix_fd( struct dir *dir )
{
    return get_unix_fd( dir->fd );
}

static struct security_descriptor *dir_get_sd( struct object *obj )
{
    struct dir *dir = (struct dir *)obj;
    int unix_fd;
    struct stat st;
    struct security_descriptor *sd;
    assert( obj->ops == &dir_ops );

    unix_fd = get_dir_unix_fd( dir );

    if (unix_fd == -1 || fstat( unix_fd, &st ) == -1)
        return obj->sd;

    /* mode and uid the same? if so, no need to re-generate security descriptor */
    if (obj->sd &&
        (st.st_mode & (S_IRWXU|S_IRWXO)) == (dir->mode & (S_IRWXU|S_IRWXO)) &&
        (st.st_uid == dir->uid))
        return obj->sd;

    sd = mode_to_sd( st.st_mode,
                     security_unix_uid_to_sid( st.st_uid ),
                     token_get_primary_group( current->process->token ));
    if (!sd) return obj->sd;

    dir->mode = st.st_mode;
    dir->uid = st.st_uid;
    free( obj->sd );
    obj->sd = sd;
    return sd;
}

static int dir_set_sd( struct object *obj, const struct security_descriptor *sd,
                       unsigned int set_info )
{
    struct dir *dir = (struct dir *)obj;
    const SID *owner;
322
    struct stat st;
323 324 325 326 327 328 329
    mode_t mode;
    int unix_fd;

    assert( obj->ops == &dir_ops );

    unix_fd = get_dir_unix_fd( dir );

330
    if (unix_fd == -1 || fstat( unix_fd, &st ) == -1) return 1;
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352

    if (set_info & OWNER_SECURITY_INFORMATION)
    {
        owner = sd_get_owner( sd );
        if (!owner)
        {
            set_error( STATUS_INVALID_SECURITY_DESCR );
            return 0;
        }
        if (!obj->sd || !security_equal_sid( owner, sd_get_owner( obj->sd ) ))
        {
            /* FIXME: get Unix uid and call fchown */
        }
    }
    else if (obj->sd)
        owner = sd_get_owner( obj->sd );
    else
        owner = token_get_user( current->process->token );

    if (set_info & DACL_SECURITY_INFORMATION)
    {
        /* keep the bits that we don't map to access rights in the ACL */
353
        mode = st.st_mode & (S_ISUID|S_ISGID|S_ISVTX);
354 355
        mode |= sd_to_mode( sd, owner );

356
        if (((st.st_mode ^ mode) & (S_IRWXU|S_IRWXG|S_IRWXO)) && fchmod( unix_fd, mode ) == -1)
357
        {
358 359
            file_set_error();
            return 0;
360 361 362 363 364
        }
    }
    return 1;
}

365 366 367 368 369 370 371 372
static struct change_record *get_first_change_record( struct dir *dir )
{
    struct list *ptr = list_head( &dir->change_records );
    if (!ptr) return NULL;
    list_remove( ptr );
    return LIST_ENTRY( ptr, struct change_record, entry );
}

373 374
static void dir_destroy( struct object *obj )
{
375
    struct change_record *record;
376 377
    struct dir *dir = (struct dir *)obj;
    assert (obj->ops == &dir_ops);
378

379 380 381
    if (dir->filter)
        remove_change( dir );

382 383 384 385 386
    if (dir->inode)
    {
        list_remove( &dir->in_entry );
        free_inode( dir->inode );
    }
387

388 389
    while ((record = get_first_change_record( dir ))) free( record );

390
    release_object( dir->fd );
391 392 393 394 395 396

    if (inotify_fd && list_empty( &change_list ))
    {
        release_object( inotify_fd );
        inotify_fd = NULL;
    }
397 398
}

399
struct dir *get_dir_obj( struct process *process, obj_handle_t handle, unsigned int access )
400 401 402 403 404 405 406
{
    return (struct dir *)get_handle_obj( process, handle, access, &dir_ops );
}

static int dir_get_poll_events( struct fd *fd )
{
    return 0;
407 408
}

409
static enum server_fd_type dir_get_fd_type( struct fd *fd )
410
{
411
    return FD_TYPE_DIR;
412 413
}

414 415
#ifdef USE_INOTIFY

416 417 418
#define HASH_SIZE 31

struct inode {
419 420 421
    struct list ch_entry;    /* entry in the children list */
    struct list children;    /* children of this inode */
    struct inode *parent;    /* parent of this inode */
422 423 424 425 426 427
    struct list dirs;        /* directory handles watching this inode */
    struct list ino_entry;   /* entry in the inode hash */
    struct list wd_entry;    /* entry in the watch descriptor hash */
    dev_t dev;               /* device number */
    ino_t ino;               /* device's inode number */
    int wd;                  /* inotify's watch descriptor */
428
    char *name;              /* basename name of the inode */
429 430
};

431 432
static struct list inode_hash[ HASH_SIZE ];
static struct list wd_hash[ HASH_SIZE ];
433

434 435
static int inotify_add_dir( char *path, unsigned int filter );

436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
static struct inode *inode_from_wd( int wd )
{
    struct list *bucket = &wd_hash[ wd % HASH_SIZE ];
    struct inode *inode;

    LIST_FOR_EACH_ENTRY( inode, bucket, struct inode, wd_entry )
        if (inode->wd == wd)
            return inode;

    return NULL;
}

static inline struct list *get_hash_list( dev_t dev, ino_t ino )
{
    return &inode_hash[ (ino ^ dev) % HASH_SIZE ];
}

453
static struct inode *find_inode( dev_t dev, ino_t ino )
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
{
    struct list *bucket = get_hash_list( dev, ino );
    struct inode *inode;

    LIST_FOR_EACH_ENTRY( inode, bucket, struct inode, ino_entry )
        if (inode->ino == ino && inode->dev == dev)
             return inode;

    return NULL;
}

static struct inode *create_inode( dev_t dev, ino_t ino )
{
    struct inode *inode;

    inode = malloc( sizeof *inode );
    if (inode)
    {
472
        list_init( &inode->children );
473 474 475 476
        list_init( &inode->dirs );
        inode->ino = ino;
        inode->dev = dev;
        inode->wd = -1;
477 478
        inode->parent = NULL;
        inode->name = NULL;
479 480 481 482 483
        list_add_tail( get_hash_list( dev, ino ), &inode->ino_entry );
    }
    return inode;
}

484 485 486 487 488 489 490 491 492 493
static struct inode *get_inode( dev_t dev, ino_t ino )
{
    struct inode *inode;

    inode = find_inode( dev, ino );
    if (inode)
        return inode;
    return create_inode( dev, ino );
}

494 495 496 497 498 499 500 501
static void inode_set_wd( struct inode *inode, int wd )
{
    if (inode->wd != -1)
        list_remove( &inode->wd_entry );
    inode->wd = wd;
    list_add_tail( &wd_hash[ wd % HASH_SIZE ], &inode->wd_entry );
}

502 503
static void inode_set_name( struct inode *inode, const char *name )
{
504
    free (inode->name);
505 506 507
    inode->name = name ? strdup( name ) : NULL;
}

508 509
static void free_inode( struct inode *inode )
{
510
    int subtree = 0, watches = 0;
511
    struct inode *tmp, *next;
512 513 514 515 516 517 518 519 520 521
    struct dir *dir;

    LIST_FOR_EACH_ENTRY( dir, &inode->dirs, struct dir, in_entry )
    {
        subtree |= dir->subtree;
        watches++;
    }

    if (!subtree && !inode->parent)
    {
522 523
        LIST_FOR_EACH_ENTRY_SAFE( tmp, next, &inode->children,
                                  struct inode, ch_entry )
524 525 526 527 528 529 530 531
        {
            assert( tmp != inode );
            assert( tmp->parent == inode );
            free_inode( tmp );
        }
    }

    if (watches)
532 533
        return;

534 535 536
    if (inode->parent)
        list_remove( &inode->ch_entry );

537 538 539 540 541 542 543
    /* disconnect remaining children from the parent */
    LIST_FOR_EACH_ENTRY_SAFE( tmp, next, &inode->children, struct inode, ch_entry )
    {
        list_remove( &tmp->ch_entry );
        tmp->parent = NULL;
    }

544 545
    if (inode->wd != -1)
    {
546
        inotify_rm_watch( get_unix_fd( inotify_fd ), inode->wd );
547 548 549
        list_remove( &inode->wd_entry );
    }
    list_remove( &inode->ino_entry );
550 551

    free( inode->name );
552 553 554
    free( inode );
}

555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584
static struct inode *inode_add( struct inode *parent,
                                dev_t dev, ino_t ino, const char *name )
{
    struct inode *inode;
 
    inode = get_inode( dev, ino );
    if (!inode)
        return NULL;
 
    if (!inode->parent)
    {
        list_add_tail( &parent->children, &inode->ch_entry );
        inode->parent = parent;
        assert( inode != parent );
    }
    inode_set_name( inode, name );

    return inode;
}

static struct inode *inode_from_name( struct inode *inode, const char *name )
{
    struct inode *i;

    LIST_FOR_EACH_ENTRY( i, &inode->children, struct inode, ch_entry )
        if (i->name && !strcmp( i->name, name ))
            return i;
    return NULL;
}

585 586 587 588 589
static int inotify_get_poll_events( struct fd *fd );
static void inotify_poll_event( struct fd *fd, int event );

static const struct fd_ops inotify_fd_ops =
{
590 591
    inotify_get_poll_events,     /* get_poll_events */
    inotify_poll_event,          /* poll_event */
592 593
    NULL,                        /* flush */
    NULL,                        /* get_fd_type */
594
    NULL,                        /* ioctl */
595 596 597
    NULL,                        /* queue_async */
    NULL,                        /* reselect_async */
    NULL,                        /* cancel_async */
598 599 600 601 602 603 604
};

static int inotify_get_poll_events( struct fd *fd )
{
    return POLLIN;
}

605
static void inotify_do_change_notify( struct dir *dir, unsigned int action,
606
                                      unsigned int cookie, const char *relpath )
607
{
608 609
    struct change_record *record;

610 611
    assert( dir->obj.ops == &dir_ops );

612 613
    if (dir->want_data)
    {
614
        size_t len = strlen(relpath);
615
        record = malloc( offsetof(struct change_record, event.name[len]) );
616 617 618
        if (!record)
            return;

619
        record->cookie = cookie;
620 621 622
        record->event.action = action;
        memcpy( record->event.name, relpath, len );
        record->event.len = len;
623 624 625

        list_add_tail( &dir->change_records, &record->entry );
    }
626

627
    fd_async_wake_up( dir->fd, ASYNC_TYPE_WAIT, STATUS_ALERTED );
628 629
}

630 631 632 633 634 635 636 637 638 639 640 641 642 643 644
static unsigned int filter_from_event( struct inotify_event *ie )
{
    unsigned int filter = 0;

    if (ie->mask & (IN_MOVED_FROM | IN_MOVED_TO | IN_DELETE | IN_CREATE))
        filter |= FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME;
    if (ie->mask & IN_MODIFY)
        filter |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE;
    if (ie->mask & IN_ATTRIB)
        filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SECURITY;
    if (ie->mask & IN_ACCESS)
        filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
    if (ie->mask & IN_CREATE)
        filter |= FILE_NOTIFY_CHANGE_CREATION;

645 646 647 648 649
    if (ie->mask & IN_ISDIR)
        filter &= ~FILE_NOTIFY_CHANGE_FILE_NAME;
    else
        filter &= ~FILE_NOTIFY_CHANGE_DIR_NAME;

650 651 652
    return filter;
}

653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672
/* scan up the parent directories for watches */
static unsigned int filter_from_inode( struct inode *inode, int is_parent )
{
    unsigned int filter = 0;
    struct dir *dir;

    /* combine filters from parents watching subtrees */
    while (inode)
    {
        LIST_FOR_EACH_ENTRY( dir, &inode->dirs, struct dir, in_entry )
            if (dir->subtree || !is_parent)
                filter |= dir->filter;
        is_parent = 1;
        inode = inode->parent;
    }

    return filter;
}

static char *inode_get_path( struct inode *inode, int sz )
673 674
{
    struct list *head;
675 676 677 678 679
    char *path;
    int len;

    if (!inode)
        return NULL;
680 681

    head = list_head( &inode->dirs );
682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704
    if (head)
    {
        int unix_fd = get_unix_fd( LIST_ENTRY( head, struct dir, in_entry )->fd );
        path = malloc ( 32 + sz );
        if (path)
            sprintf( path, "/proc/self/fd/%u/", unix_fd );
        return path;
    }

    if (!inode->name)
        return NULL;

    len = strlen( inode->name );
    path = inode_get_path( inode->parent, sz + len + 1 );
    if (!path)
        return NULL;
    
    strcat( path, inode->name );
    strcat( path, "/" );

    return path;
}

705
static void inode_check_dir( struct inode *parent, const char *name )
706 707 708 709 710
{
    char *path;
    unsigned int filter;
    struct inode *inode;
    struct stat st;
711
    int wd = -1;
712

713
    path = inode_get_path( parent, strlen(name) );
714
    if (!path)
715
        return;
716 717

    strcat( path, name );
718

719
    if (stat( path, &st ) < 0)
720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736
        goto end;

    filter = filter_from_inode( parent, 1 );
    if (!filter)
        goto end;

    inode = inode_add( parent, st.st_dev, st.st_ino, name );
    if (!inode || inode->wd != -1)
        goto end;

    wd = inotify_add_dir( path, filter );
    if (wd != -1)
        inode_set_wd( inode, wd );
    else
        free_inode( inode );

end:
737
    free( path );
738
}
739

740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764
static int prepend( char **path, const char *segment )
{
    int extra;
    char *p;

    extra = strlen( segment ) + 1;
    if (*path)
    {
        int len = strlen( *path ) + 1;
        p = realloc( *path, len + extra );
        if (!p) return 0;
        memmove( &p[ extra ], p, len );
        p[ extra - 1 ] = '/';
        memcpy( p, segment, extra - 1 );
    }
    else
    {
        p = malloc( extra );
        if (!p) return 0;
        memcpy( p, segment, extra );
    }

    *path = p;

    return 1;
765 766
}

767 768
static void inotify_notify_all( struct inotify_event *ie )
{
769
    unsigned int filter, action;
770 771
    struct inode *inode, *i;
    char *path = NULL;
772 773 774 775 776 777 778 779 780 781
    struct dir *dir;

    inode = inode_from_wd( ie->wd );
    if (!inode)
    {
        fprintf( stderr, "no inode matches %d\n", ie->wd);
        return;
    }

    filter = filter_from_event( ie );
782 783 784
    
    if (ie->mask & IN_CREATE)
    {
785 786 787
        if (ie->mask & IN_ISDIR)
            inode_check_dir( inode, ie->name );

788 789 790 791
        action = FILE_ACTION_ADDED;
    }
    else if (ie->mask & IN_DELETE)
        action = FILE_ACTION_REMOVED;
792 793 794 795
    else if (ie->mask & IN_MOVED_FROM)
        action = FILE_ACTION_RENAMED_OLD_NAME;
    else if (ie->mask & IN_MOVED_TO)
        action = FILE_ACTION_RENAMED_NEW_NAME;
796 797
    else
        action = FILE_ACTION_MODIFIED;
798

799
    /*
800
     * Work our way up the inode hierarchy
801
     *  extending the relative path as we go
802
     *  and notifying all recursive watches.
803 804 805 806 807 808 809 810
     */
    if (!prepend( &path, ie->name ))
        return;

    for (i = inode; i; i = i->parent)
    {
        LIST_FOR_EACH_ENTRY( dir, &i->dirs, struct dir, in_entry )
            if ((filter & dir->filter) && (i==inode || dir->subtree))
811
                inotify_do_change_notify( dir, action, ie->cookie, path );
812 813 814 815 816 817 818 819 820 821 822 823 824

        if (!i->name || !prepend( &path, i->name ))
            break;
    }

    free( path );

    if (ie->mask & IN_DELETE)
    {
        i = inode_from_name( inode, ie->name );
        if (i)
            free_inode( i );
    }
825 826
}

827 828 829 830 831 832 833 834 835 836 837 838 839 840
static void inotify_poll_event( struct fd *fd, int event )
{
    int r, ofs, unix_fd;
    char buffer[0x1000];
    struct inotify_event *ie;

    unix_fd = get_unix_fd( fd );
    r = read( unix_fd, buffer, sizeof buffer );
    if (r < 0)
    {
        fprintf(stderr,"inotify_poll_event(): inotify read failed!\n");
        return;
    }

841
    for( ofs = 0; ofs < r - offsetof(struct inotify_event, name); )
842 843 844 845
    {
        ie = (struct inotify_event*) &buffer[ofs];
        if (!ie->len)
            break;
846 847
        ofs += offsetof( struct inotify_event, name[ie->len] );
        if (ofs > r) break;
848
        inotify_notify_all( ie );
849 850 851
    }
}

852
static inline struct fd *create_inotify_fd( void )
853 854 855 856 857 858
{
    int unix_fd;

    unix_fd = inotify_init();
    if (unix_fd<0)
        return NULL;
859
    return create_anonymous_fd( &inotify_fd_ops, unix_fd, NULL, 0 );
860 861
}

862
static int map_flags( unsigned int filter )
863
{
864 865 866 867
    unsigned int mask;

    /* always watch these so we can track subdirectories in recursive watches */
    mask = (IN_MOVED_FROM | IN_MOVED_TO | IN_DELETE | IN_CREATE | IN_DELETE_SELF);
868

869 870 871 872 873 874 875 876 877 878 879
    if (filter & FILE_NOTIFY_CHANGE_ATTRIBUTES)
        mask |= IN_ATTRIB;
    if (filter & FILE_NOTIFY_CHANGE_SIZE)
        mask |= IN_MODIFY;
    if (filter & FILE_NOTIFY_CHANGE_LAST_WRITE)
        mask |= IN_MODIFY;
    if (filter & FILE_NOTIFY_CHANGE_LAST_ACCESS)
        mask |= IN_ACCESS;
    if (filter & FILE_NOTIFY_CHANGE_SECURITY)
        mask |= IN_ATTRIB;

880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940
    return mask;
}

static int inotify_add_dir( char *path, unsigned int filter )
{
    int wd = inotify_add_watch( get_unix_fd( inotify_fd ),
                                path, map_flags( filter ) );
    if (wd != -1)
        set_fd_events( inotify_fd, POLLIN );
    return wd;
}

static int init_inotify( void )
{
    int i;

    if (inotify_fd)
        return 1;

    inotify_fd = create_inotify_fd();
    if (!inotify_fd)
        return 0;

    for (i=0; i<HASH_SIZE; i++)
    {
        list_init( &inode_hash[i] );
        list_init( &wd_hash[i] );
    }

    return 1;
}

static int inotify_adjust_changes( struct dir *dir )
{
    unsigned int filter;
    struct inode *inode;
    struct stat st;
    char path[32];
    int wd, unix_fd;

    if (!inotify_fd)
        return 0;

    unix_fd = get_unix_fd( dir->fd );

    inode = dir->inode;
    if (!inode)
    {
        /* check if this fd is already being watched */
        if (-1 == fstat( unix_fd, &st ))
            return 0;

        inode = get_inode( st.st_dev, st.st_ino );
        if (!inode)
            inode = create_inode( st.st_dev, st.st_ino );
        if (!inode)
            return 0;
        list_add_tail( &inode->dirs, &dir->in_entry );
        dir->inode = inode;
    }

941
    filter = filter_from_inode( inode, 0 );
942 943 944 945 946 947 948

    sprintf( path, "/proc/self/fd/%u", unix_fd );
    wd = inotify_add_dir( path, filter );
    if (wd == -1) return 0;

    inode_set_wd( inode, wd );

949
    return 1;
950 951
}

952 953 954 955 956 957 958 959
static char *get_basename( const char *link )
{
    char *buffer, *name = NULL;
    int r, n = 0x100;

    while (1)
    {
        buffer = malloc( n );
960
        if (!buffer) return NULL;
961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050

        r = readlink( link, buffer, n );
        if (r < 0)
            break;

        if (r < n)
        {
            name = buffer;
            break;
        }
        free( buffer );
        n *= 2;
    }

    if (name)
    {
        while (r > 0 && name[ r - 1 ] == '/' )
            r--;
        name[ r ] = 0;

        name = strrchr( name, '/' );
        if (name)
            name = strdup( &name[1] );
    }

    free( buffer );
    return name;
}

static int dir_add_to_existing_notify( struct dir *dir )
{
    struct inode *inode, *parent;
    unsigned int filter = 0;
    struct stat st, st_new;
    char link[35], *name;
    int wd, unix_fd;

    if (!inotify_fd)
        return 0;

    unix_fd = get_unix_fd( dir->fd );

    /* check if it's in the list of inodes we want to watch */
    if (-1 == fstat( unix_fd, &st_new ))
        return 0;
    inode = find_inode( st_new.st_dev, st_new.st_ino );
    if (inode)
        return 0;

    /* lookup the parent */
    sprintf( link, "/proc/self/fd/%u/..", unix_fd );
    if (-1 == stat( link, &st ))
        return 0;

    /*
     * If there's no parent, stop.  We could keep going adding
     *  ../ to the path until we hit the root of the tree or
     *  find a recursively watched ancestor.
     * Assume it's too expensive to search up the tree for now.
     */
    parent = find_inode( st.st_dev, st.st_ino );
    if (!parent)
        return 0;

    if (parent->wd == -1)
        return 0;

    filter = filter_from_inode( parent, 1 );
    if (!filter)
        return 0;

    sprintf( link, "/proc/self/fd/%u", unix_fd );
    name = get_basename( link );
    if (!name)
        return 0;
    inode = inode_add( parent, st_new.st_dev, st_new.st_ino, name );
    free( name );
    if (!inode)
        return 0;

    /* Couldn't find this inode at the start of the function, must be new */
    assert( inode->wd == -1 );

    wd = inotify_add_dir( link, filter );
    if (wd != -1)
        inode_set_wd( inode, wd );

    return 1;
}

1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067
#else

static int init_inotify( void )
{
    return 0;
}

static int inotify_adjust_changes( struct dir *dir )
{
    return 0;
}

static void free_inode( struct inode *inode )
{
    assert( 0 );
}

1068 1069 1070 1071 1072
static int dir_add_to_existing_notify( struct dir *dir )
{
    return 0;
}

1073
#endif  /* USE_INOTIFY */
1074

1075
struct object *create_dir_obj( struct fd *fd, unsigned int access, mode_t mode )
1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089
{
    struct dir *dir;

    dir = alloc_object( &dir_ops );
    if (!dir)
        return NULL;

    list_init( &dir->change_records );
    dir->filter = 0;
    dir->notified = 0;
    dir->want_data = 0;
    dir->inode = NULL;
    grab_object( fd );
    dir->fd = fd;
1090 1091
    dir->mode = mode;
    dir->uid  = ~(uid_t)0;
1092 1093
    set_fd_user( fd, &dir_fd_ops, &dir->obj );

1094 1095
    dir_add_to_existing_notify( dir );

1096 1097 1098
    return &dir->obj;
}

1099 1100 1101 1102
/* enable change notifications for a directory */
DECL_HANDLER(read_directory_changes)
{
    struct dir *dir;
1103
    struct async *async;
1104 1105 1106 1107 1108 1109 1110

    if (!req->filter)
    {
        set_error(STATUS_INVALID_PARAMETER);
        return;
    }

1111
    dir = get_dir_obj( current->process, req->async.handle, 0 );
1112 1113
    if (!dir)
        return;
1114

1115
    /* requests don't timeout */
1116
    if (!(async = fd_queue_async( dir->fd, &req->async, ASYNC_TYPE_WAIT ))) goto end;
1117

1118 1119 1120
    /* assign it once */
    if (!dir->filter)
    {
1121
        init_inotify();
1122 1123
        insert_change( dir );
        dir->filter = req->filter;
1124
        dir->subtree = req->subtree;
1125
        dir->want_data = req->want_data;
1126 1127
    }

1128
    /* if there's already a change in the queue, send it */
1129
    if (!list_empty( &dir->change_records ))
1130
        fd_async_wake_up( dir->fd, ASYNC_TYPE_WAIT, STATUS_ALERTED );
1131

1132
    /* setup the real notification */
1133
    if (!inotify_adjust_changes( dir ))
1134
        dnotify_adjust_changes( dir );
1135

1136
    release_object( async );
1137 1138 1139 1140
    set_error(STATUS_PENDING);

end:
    release_object( dir );
1141
}
1142 1143 1144

DECL_HANDLER(read_change)
{
1145
    struct change_record *record, *next;
1146
    struct dir *dir;
1147 1148 1149
    struct list events;
    char *data, *event;
    int size = 0;
1150 1151 1152 1153 1154

    dir = get_dir_obj( current->process, req->handle, 0 );
    if (!dir)
        return;

1155 1156 1157 1158 1159
    list_init( &events );
    list_move_tail( &events, &dir->change_records );
    release_object( dir );

    if (list_empty( &events ))
1160 1161
    {
        set_error( STATUS_NO_DATA_DETECTED );
1162 1163
        return;
    }
1164

1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178
    LIST_FOR_EACH_ENTRY( record, &events, struct change_record, entry )
    {
        size += (offsetof(struct filesystem_event, name[record->event.len])
                + sizeof(int)-1) / sizeof(int) * sizeof(int);
    }

    if (size > get_reply_max_size())
        set_error( STATUS_BUFFER_TOO_SMALL );
    else if ((data = mem_alloc( size )) != NULL)
    {
        event = data;
        LIST_FOR_EACH_ENTRY( record, &events, struct change_record, entry )
        {
            data_size_t len = offsetof( struct filesystem_event, name[record->event.len] );
1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194

            /* FIXME: rename events are sometimes reported as delete/create */
            if (record->event.action == FILE_ACTION_RENAMED_OLD_NAME)
            {
                struct list *elem = list_next( &events, &record->entry );
                if (elem)
                    next = LIST_ENTRY(elem, struct change_record, entry);

                if (elem && next->cookie == record->cookie)
                    next->cookie = 0;
                else
                    record->event.action = FILE_ACTION_REMOVED;
            }
            else if (record->event.action == FILE_ACTION_RENAMED_NEW_NAME && record->cookie)
                record->event.action = FILE_ACTION_ADDED;

1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210
            memcpy( event, &record->event, len );
            event += len;
            if (len % sizeof(int))
            {
                memset( event, 0, sizeof(int) - len % sizeof(int) );
                event += sizeof(int) - len % sizeof(int);
            }
        }
        set_reply_data_ptr( data, size );
    }

    LIST_FOR_EACH_ENTRY_SAFE( record, next, &events, struct change_record, entry )
    {
        list_remove( &record->entry );
        free( record );
    }
1211
}