// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "update_engine/filesystem_iterator.h"
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <set>
#include <string>
#include <vector>
#include "base/logging.h"
#include "update_engine/utils.h"

using std::set;
using std::string;
using std::vector;

namespace chromeos_update_engine {

// We use a macro here for two reasons:
// 1. We want to be able to return from the caller's function.
// 2. We can use the #macro_arg ism to get a string of the calling statement,
//    which we can log.

#define RETURN_ERROR_IF_FALSE(_statement)                               \
  do {                                                                  \
    bool _result = (_statement);                                        \
    if (!_result) {                                                     \
      string _message = utils::ErrnoNumberAsString(errno);              \
      LOG(INFO) << #_statement << " failed: " << _message << ". Aborting"; \
      is_end_ = true;                                                   \
      is_err_ = true;                                                   \
      return;                                                           \
    }                                                                   \
  } while (0)

FilesystemIterator::FilesystemIterator(
    const std::string& path,
    const std::set<std::string>& excl_prefixes)
    : excl_prefixes_(excl_prefixes),
      is_end_(false),
      is_err_(false) {
  root_path_ = utils::NormalizePath(path, true);
  RETURN_ERROR_IF_FALSE(lstat(root_path_.c_str(), &stbuf_) == 0);
  root_dev_ = stbuf_.st_dev;
}

FilesystemIterator::~FilesystemIterator() {
  for (vector<DIR*>::iterator it = dirs_.begin(); it != dirs_.end(); ++it) {
    LOG_IF(ERROR, closedir(*it) != 0) << "closedir failed";
  }
}

// Returns full path for current file
std::string FilesystemIterator::GetFullPath() const {
  return root_path_ + GetPartialPath();
}

std::string FilesystemIterator::GetPartialPath() const {
  std::string ret;
  for (vector<string>::const_iterator it = names_.begin();
       it != names_.end(); ++it) {
    ret += "/";
    ret += *it;
  }
  return ret;
}

// Increments to the next file
void FilesystemIterator::Increment() {
  // If we're currently on a dir, descend into children, but only if
  // we're on the same device as the root device

  bool entering_dir = false;  // true if we're entering into a new dir
  if (S_ISDIR(stbuf_.st_mode) && (stbuf_.st_dev == root_dev_)) {
    DIR* dir = opendir(GetFullPath().c_str());
    if ((!dir) && ((errno == ENOTDIR) || (errno == ENOENT))) {
      // opendir failed b/c either it's not a dir or it doesn't exist.
      // that's fine. let's just skip over this.
      LOG(ERROR) << "Can't descend into " << GetFullPath();
    } else {
      RETURN_ERROR_IF_FALSE(dir);
      entering_dir = true;
      dirs_.push_back(dir);
    }
  }

  if (!entering_dir && names_.empty()) {
    // root disappeared while we tried to descend into it
    is_end_ = true;
    return;
  }

  if (!entering_dir)
    names_.pop_back();

  IncrementInternal();
  for (set<string>::const_iterator it = excl_prefixes_.begin();
       it != excl_prefixes_.end(); ++it) {
    if (utils::StringHasPrefix(GetPartialPath(), *it)) {
      Increment();
      break;
    }
  }
  return;
}

// Assumes that we need to find the next child of dirs_.back(), or if
// there are none more, go up the chain
void FilesystemIterator::IncrementInternal() {
  CHECK_EQ(dirs_.size(), names_.size() + 1);
  for (;;) {
    struct dirent dir_entry;
    struct dirent* dir_entry_pointer;
    int r;
    RETURN_ERROR_IF_FALSE(
        (r = readdir_r(dirs_.back(), &dir_entry, &dir_entry_pointer)) == 0);
    if (dir_entry_pointer) {
      // Found an entry
      names_.push_back(dir_entry_pointer->d_name);
      // Validate
      RETURN_ERROR_IF_FALSE(lstat(GetFullPath().c_str(), &stbuf_) == 0);
      if (strcmp(dir_entry_pointer->d_name, ".") &&
          strcmp(dir_entry_pointer->d_name, "..")) {
        // Done
        return;
      }
      // Child didn't work out. Try again
      names_.pop_back();
    } else {
      // No more children in this dir. Pop it and try again
      RETURN_ERROR_IF_FALSE(closedir(dirs_.back()) == 0);
      dirs_.pop_back();
      if (dirs_.empty()) {
        CHECK(names_.empty());
        // Done with the entire iteration
        is_end_ = true;
        return;
      }
      CHECK(!names_.empty());
      names_.pop_back();
    }
  }
}

}  //   namespace chromeos_update_engine
