1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   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
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130

content / browser / file_system_access / file_path_watcher / file_path_watcher_kqueue.h [blame]

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_BROWSER_FILE_SYSTEM_ACCESS_FILE_PATH_WATCHER_FILE_PATH_WATCHER_KQUEUE_H_
#define CONTENT_BROWSER_FILE_SYSTEM_ACCESS_FILE_PATH_WATCHER_FILE_PATH_WATCHER_KQUEUE_H_

#include <sys/event.h>

#include <memory>
#include <vector>

#include "base/files/file_descriptor_watcher_posix.h"
#include "base/files/file_path.h"
#include "content/browser/file_system_access/file_path_watcher/file_path_watcher.h"

namespace content {

// Mac-specific file watcher implementation based on kqueue.
// The Linux and Windows versions are able to detect:
// - file creation/deletion/modification in a watched directory
// - file creation/deletion/modification for a watched file
// - modifications to the paths to a watched object that would affect the
//   object such as renaming/attribute changes etc.
// The kqueue implementation will handle all of the items in the list above
// except for detecting modifications to files in a watched directory. It will
// detect the creation and deletion of files, just not the modification of
// files. It does however detect the attribute changes that the FSEvents impl
// would miss.
class FilePathWatcherKQueue : public FilePathWatcher::PlatformDelegate {
 public:
  FilePathWatcherKQueue();
  FilePathWatcherKQueue(const FilePathWatcherKQueue&) = delete;
  FilePathWatcherKQueue& operator=(const FilePathWatcherKQueue&) = delete;
  ~FilePathWatcherKQueue() override;

  // FilePathWatcher::PlatformDelegate overrides.
  bool Watch(const base::FilePath& path,
             Type type,
             const FilePathWatcher::Callback& callback) override;
  void Cancel() override;

 private:
  class EventData {
   public:
    EventData(const base::FilePath& path,
              const base::FilePath::StringType& subdir)
        : path_(path), subdir_(subdir) {}
    base::FilePath path_;                // Full path to this item.
    base::FilePath::StringType subdir_;  // Path to any sub item.
  };

  typedef std::vector<struct kevent> EventVector;

  // Called when data is available in |kqueue_|.
  void OnKQueueReadable();

  // Returns true if the kevent values are error free.
  bool AreKeventValuesValid(struct kevent* kevents, int count);

  // Respond to a change of attributes of the path component represented by
  // |event|. Sets |target_file_affected| to true if |target_| is affected.
  // Sets |update_watches| to true if |events_| need to be updated.
  void HandleAttributesChange(const EventVector::iterator& event,
                              bool* target_file_affected,
                              bool* update_watches);

  // Respond to a move or deletion of the path component represented by
  // |event|. Sets |target_file_affected| to true if |target_| is affected.
  // Sets |update_watches| to true if |events_| need to be updated.
  void HandleDeleteOrMoveChange(const EventVector::iterator& event,
                                bool* target_file_affected,
                                bool* update_watches);

  // Respond to a creation of an item in the path component represented by
  // |event|. Sets |target_file_affected| to true if |target_| is affected.
  // Sets |update_watches| to true if |events_| need to be updated.
  void HandleCreateItemChange(const EventVector::iterator& event,
                              bool* target_file_affected,
                              bool* update_watches);

  // Update |events_| with the current status of the system.
  // Sets |target_file_affected| to true if |target_| is affected.
  // Returns false if an error occurs.
  bool UpdateWatches(bool* target_file_affected);

  // Fills |events| with one kevent per component in |path|.
  // Returns the number of valid events created where a valid event is
  // defined as one that has a ident (file descriptor) field != -1.
  static size_t EventsForPath(base::FilePath path, EventVector* events);

  // Fills |events| with one kevent for |path|. Returns 1 if a file descriptor
  // for |path| could be created, or 0 otherwise.
  static size_t EventForItem(const base::FilePath& path, EventVector* events);

  // Release a kevent generated by EventsForPath.
  static void ReleaseEvent(struct kevent& event);

  // Returns a file descriptor that will not block the system from deleting
  // the file it references.
  static uintptr_t FileDescriptorForPath(const base::FilePath& path);

  static const uintptr_t kNoFileDescriptor = static_cast<uintptr_t>(-1);

  // Closes |*fd| and sets |*fd| to -1.
  static void CloseFileDescriptor(uintptr_t* fd);

  // Returns true if kevent has open file descriptor.
  static bool IsKeventFileDescriptorOpen(const struct kevent& event) {
    return event.ident != kNoFileDescriptor;
  }

  static EventData* EventDataForKevent(const struct kevent& event) {
    return reinterpret_cast<EventData*>(event.udata);
  }

  EventVector events_;
  FilePathWatcher::Callback callback_;
  base::FilePath target_;
  int kqueue_;

  // Throughout the lifetime of this, OnKQueueReadable() will be called when
  // data is available in |kqueue_|.
  std::unique_ptr<base::FileDescriptorWatcher::Controller>
      kqueue_watch_controller_;
};

}  // namespace content

#endif  // CONTENT_BROWSER_FILE_SYSTEM_ACCESS_FILE_PATH_WATCHER_FILE_PATH_WATCHER_KQUEUE_H_