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
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150

base / files / important_file_writer_cleaner.h [blame]

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

#ifndef BASE_FILES_IMPORTANT_FILE_WRITER_CLEANER_H_
#define BASE_FILES_IMPORTANT_FILE_WRITER_CLEANER_H_

#include <atomic>
#include <vector>

#include "base/base_export.h"
#include "base/containers/flat_set.h"
#include "base/files/file_path.h"
#include "base/memory/scoped_refptr.h"
#include "base/no_destructor.h"
#include "base/numerics/clamped_math.h"
#include "base/sequence_checker.h"
#include "base/synchronization/lock.h"
#include "base/thread_annotations.h"
#include "base/time/time.h"

namespace base {

class SequencedTaskRunner;

// A cleaner for forgotten .tmp files left behind by ImportantFileWriter; see
// https://crbug.com/1075917.
//
// ImportantFileWriter has the potential to leak .tmp files in case of a crash
// or power failure during processing, or in case of interference by third-party
// software. This class implements a singleton that makes a single scan over
// given directories to delete any *.tmp files older than the current process.
// Processes that use ImportantFileWriter are expected to call the instance's
// Start method at some point during startup to enable the cleaner.
// ImportantFileWriter calls the AddDirectory method to provide the directory
// hosting an "important" file. Hosting processes are expected to call the Stop
// method at shutdown.
//
// The deletion scan takes place in a background task.
class BASE_EXPORT ImportantFileWriterCleaner {
 public:
  // Gets the process-wide single instance of the cleaner.
  static ImportantFileWriterCleaner& GetInstance();

  ImportantFileWriterCleaner(const ImportantFileWriterCleaner&) = delete;
  ImportantFileWriterCleaner& operator=(const ImportantFileWriterCleaner&) =
      delete;
  ~ImportantFileWriterCleaner() = delete;

  // Adds |directory| to the set to be cleaned if it has not already been
  // handled. If the Start method has already been called, the cleaner will
  // begin processing |directory| after all others that have previously been
  // added have been cleaned (immediately, if there are no others). Any calls to
  // this method prior to Initialize are ignored.
  static void AddDirectory(const FilePath& directory);

  // Initializes the instance on the hosting process's main sequence (the one on
  // which Start and Stop will ultimately be called). It is safe to call this
  // any number of times from the main sequence.
  void Initialize();

  // Starts the instance. If any directories have already been added, the
  // background task is posted immediately to begin processing them. Otherwise,
  // the next call to AddDirectory will begin processing.
  void Start();

  // Stops the instance. The background task, if it is active, is notified to
  // halt processing and return.
  void Stop();

  // Brings the instance back to the uninitialized state. This should be used in
  // tests that call Initialize so that the instance forgets about the test's
  // main thread task runner.
  void UninitializeForTesting();

  // Returns the upper-bound time. Files with modification times older than this
  // are assumed to have been orphaned by a previous instance of the process.
  base::Time GetUpperBoundTimeForTest() const;

 private:
  friend class NoDestructor<ImportantFileWriterCleaner>;

  ImportantFileWriterCleaner();

  // True once Start() has been called; false following Stop();
  bool is_started() const {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    return started_;
  }

  // True once the background task has been posted; false once it returns.
  bool is_running() const {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    return running_;
  }

  // The workhorse for AddDirectory.
  void AddDirectoryImpl(const FilePath& directory);

  // Schedules the background task to run, processing all directories that have
  // accumulated.
  void ScheduleTask();

  // Iterates over the contents of |directories|, deleting all *.tmp files older
  // than |upper_bound_time|. Checks |stop_flag| after each deletion to see if
  // the instance has been stopped by the host process. Returns false if
  // processing was interrupted by |stop_flag| having been set, or true
  // indicating that all directories were fully processed.
  static bool CleanInBackground(Time upper_bound_time,
                                std::vector<FilePath> directories,
                                std::atomic_bool& stop_flag);

  // Cleans up after completion of the background task. |processing_completed|
  // is true when all directories were fully processed, or false if the task
  // potentially exited early in response to Stop().
  void OnBackgroundTaskFinished(bool processing_completed);

  // Finalizes a request to stop after the background task returns.
  void DoStop();

  // Provides exclusive access to the instance's task runner.
  Lock task_runner_lock_;

  // The hosting process's main thread task runner.
  scoped_refptr<SequencedTaskRunner> task_runner_ GUARDED_BY(task_runner_lock_);

  // The time before which any discovered temporary file is presumed to be
  // unused, and therefore safe to delete.
  const Time upper_bound_time_;

  // The set of all directories hosting files written by an ImportantFileWriter.
  flat_set<FilePath> important_directories_
      GUARDED_BY_CONTEXT(sequence_checker_);

  // Directories added to the instance waiting either for a call to Start() or
  // waiting for an existing background task to complete.
  std::vector<FilePath> pending_directories_
      GUARDED_BY_CONTEXT(sequence_checker_);

  std::atomic_bool stop_flag_{false};

  bool started_ GUARDED_BY_CONTEXT(sequence_checker_) = false;
  bool running_ GUARDED_BY_CONTEXT(sequence_checker_) = false;

  SEQUENCE_CHECKER(sequence_checker_);
};

}  // namespace base

#endif  // BASE_FILES_IMPORTANT_FILE_WRITER_CLEANER_H_