/*
 * Copyright (c) 2000-2001, 2003 Apple Computer, Inc. All Rights Reserved.
 * 
 * The contents of this file constitute Original Code as defined in and are
 * subject to the Apple Public Source License Version 1.2 (the 'License').
 * You may not use this file except in compliance with the License. Please obtain
 * a copy of the License at http://www.apple.com/publicsource and read it before
 * using this file.
 * 
 * This Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
 * specific language governing rights and limitations under the License.
 */


//
//  AtomicFile.h - Description t.b.d.
//
#ifndef _SECURITY_ATOMICFILE_H_
#define _SECURITY_ATOMICFILE_H_  1

#include <security_utilities/refcount.h>
#include <Security/cssm.h>
#include <string>
#include <sys/stat.h>

namespace Security
{

class AtomicBufferedFile;
class AtomicLockedFile;
class AtomicTempFile;

class AtomicFile
{
public:
	AtomicFile(const std::string &inPath);
	~AtomicFile();

    // Aquire the write lock and remove the file.
    void performDelete();

    // Aquire the write lock and rename the file.
    void rename(const std::string &inNewPath);

	// Lock the file for writing and return a newly created AtomicTempFile.
    RefPointer<AtomicTempFile> create(mode_t mode);

    // Lock the file for writing and return a newly created AtomicTempFile.
	RefPointer<AtomicTempFile> write();

	// Return a bufferedFile containing current version of the file for reading.
	RefPointer<AtomicBufferedFile> read();

	string path() const { return mPath; }
	string dir() const { return mDir; }
	string file() const { return mFile; }

	mode_t mode() const;

    enum OffsetType
	{
        FromStart,
		FromEnd			// only works with offset of 0
    };

	static void pathSplit(const std::string &inFull, std::string &outDir, std::string &outFile);
	static void mkpath(const std::string &inDir, mode_t mode = 0777);
	static int ropen(const char *const name, int flags, mode_t mode);
	static int rclose(int fd);

private:
	string mPath;
	string mDir;
	string mFile;
};


//
// AtomicBufferedFile - This represents an instance of a file opened for reading.
// The file is read into memory and closed after this is done.
// The memory is released when this object is destroyed.
//
class AtomicBufferedFile : public RefCount
{
public:
	AtomicBufferedFile(const std::string &inPath);
	~AtomicBufferedFile();

	// Open the file and return it's size.
	off_t open();

	// Read inLength bytes starting at inOffset.
	const uint8 *read(off_t inOffset, off_t inLength, off_t &outLength);

	// Return the current mode bits of the file
	mode_t mode();

	// Close the file (this doesn't release the buffer).
	void close();

	// Return the length of the file.
	off_t length() const { return mLength; }

private:
	// Complete path to the file
	string mPath;

	// File descriptor to the file or -1 if it's not currently open.
	int mFileRef;

	// This is where the data from the file is read in to.
	uint8 *mBuffer;

	// Length of file in bytes.
	off_t mLength;
};


//
// AtomicTempFile - A temporary file to write changes to.
//
class AtomicTempFile : public RefCount
{
public:
	// Start a write for a new file.
	AtomicTempFile(AtomicFile &inFile, const RefPointer<AtomicLockedFile> &inLockedFile, mode_t mode);

	// Start a write of an existing file.
	AtomicTempFile(AtomicFile &inFile, const RefPointer<AtomicLockedFile> &inLockedFile);

	~AtomicTempFile();

    // Commit the current create or write and close the write file.
    void commit();

    void write(AtomicFile::OffsetType inOffsetType, off_t inOffset, const uint32 *inData, uint32 inCount);
    void write(AtomicFile::OffsetType inOffsetType, off_t inOffset, const uint8 *inData, uint32 inLength);
    void write(AtomicFile::OffsetType inOffsetType, off_t inOffset, const uint32 inData);

private:
	// Called by both constructors.
	void create(mode_t mode);

	// Fsync the file
	void fsync();

	// Close the file
	void close();

    // Rollback the current create or write (happens automatically if commit() isn't called before the destructor is).
    void rollback() throw();

private:
	// Our AtomicFile object.
	AtomicFile &mFile;

	RefPointer<AtomicLockedFile> mLockedFile;

	// Complete path to the file
	string mPath;

	// File descriptor to the file or -1 if it's not currently open.
	int mFileRef;

	// If this is true we unlink both mPath and mFile.path() when we rollback.
	bool mCreating;
};


// The current lock being held.
class AtomicLockedFile : public RefCount
{
public:
	// Create a write lock for inFile.
	AtomicLockedFile(AtomicFile &inFile);

	~AtomicLockedFile();

private:
	void lock(mode_t mode = (S_IRUSR|S_IRGRP|S_IROTH) /* === 0444 */);
	void unlock() throw();

private:
	std::string unique(mode_t mode);
	int rlink(const char *const old, const char *const newn, struct stat &sto);
	int myrename(const char *const old, const char *const newn);
	int xcreat(const char *const name, mode_t mode, time_t &tim);

	// The directory in which we create the lock
	string mDir;

	// Complete path to the file
	string mPath;
};


} // end namespace Security


#endif // _SECURITY_ATOMICFILE_H_
