mirror of
https://github.com/Maschell/libntfs-wiiu.git
synced 2024-12-17 23:51:50 +01:00
*Initial commit of the source version 2010-10-02 from WiiXplorer branches. For change log see the WiiXplorer branches change log.
This commit is contained in:
commit
aa43eb3138
27
AUTHORS
Normal file
27
AUTHORS
Normal file
@ -0,0 +1,27 @@
|
||||
|
||||
Present authors of ntfs-3g in alphabetical order:
|
||||
|
||||
Jean-Pierre Andre
|
||||
Alon Bar-Lev
|
||||
Dominique L Bouix
|
||||
Csaba Henk
|
||||
Bernhard Kaindl
|
||||
Erik Larsson
|
||||
Alejandro Pulver
|
||||
Szabolcs Szakacsits
|
||||
Miklos Szeredi
|
||||
|
||||
|
||||
Past authors in alphabetical order:
|
||||
|
||||
Anton Altaparmakov
|
||||
Mario Emmenlauer
|
||||
Yuval Fledel
|
||||
Yura Pakhuchiy
|
||||
Richard Russon
|
||||
|
||||
|
||||
Nintendo GameCube/Wii port authors in alphabetical order:
|
||||
|
||||
Rhys "Shareese" Koedijk
|
||||
Dimok
|
50
CREDITS
Normal file
50
CREDITS
Normal file
@ -0,0 +1,50 @@
|
||||
The following people have contributed directly or indirectly
|
||||
to the ntfs-3g project.
|
||||
|
||||
Please let ntfs-3g-devel@lists.sf.net know if you believe
|
||||
someone is missing, or if you prefer not to be listed.
|
||||
|
||||
Dominique L Bouix
|
||||
Csaba Henk
|
||||
Max Khon
|
||||
Auri Hautam<61>ki
|
||||
Gergely Erdelyi
|
||||
Anton Altaparmakov
|
||||
Peter Boross
|
||||
Don Bright
|
||||
Mario Emmenlauer
|
||||
Yuval Fledel
|
||||
Kano from Kanotix
|
||||
Roland Kletzing
|
||||
Maarten Lankhorst
|
||||
Gergely Madarasz
|
||||
Patrick McLean
|
||||
Florent Mertens
|
||||
Yura Pakhuchiy
|
||||
Miklos Szeredi
|
||||
Bartosz Taudul
|
||||
Zhanglinbao
|
||||
Wade Fitzpatrick
|
||||
Carsten Einig
|
||||
Adam Cecile
|
||||
Bruno Damour
|
||||
Ales Fruman
|
||||
Curt McDowell
|
||||
Thomas Franken
|
||||
Jonatan Lambert
|
||||
Klaus Knopper
|
||||
Zhanglinbao
|
||||
Ismail Donmez
|
||||
Laszlo Dvornik
|
||||
Pallaghy Ajtony
|
||||
Szabolcs Szakacsits
|
||||
Jean-Pierre Andre
|
||||
Alejandro Pulver
|
||||
Erik Larsson
|
||||
Alon Bar-Lev
|
||||
|
||||
The following people have contributed directly or indirectly
|
||||
to the Nintendo GameCube/Wii port of ntfs-3g.
|
||||
|
||||
Michael "Chishm" Chisholm
|
||||
rodries
|
340
LICENSE
Normal file
340
LICENSE
Normal file
@ -0,0 +1,340 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
31
Makefile
Normal file
31
Makefile
Normal file
@ -0,0 +1,31 @@
|
||||
|
||||
default: cube-release wii-release
|
||||
|
||||
all: debug release
|
||||
|
||||
debug: cube-debug wii-debug
|
||||
|
||||
release: cube-release wii-release
|
||||
|
||||
cube-debug:
|
||||
$(MAKE) -C source PLATFORM=cube BUILD=cube_debug
|
||||
|
||||
wii-debug:
|
||||
$(MAKE) -C source PLATFORM=wii BUILD=wii_debug
|
||||
|
||||
cube-release:
|
||||
$(MAKE) -C source PLATFORM=cube BUILD=cube_release
|
||||
|
||||
wii-release:
|
||||
$(MAKE) -C source PLATFORM=wii BUILD=wii_release
|
||||
|
||||
clean:
|
||||
$(MAKE) -C source clean
|
||||
|
||||
install: cube-release wii-release
|
||||
$(MAKE) -C source install
|
||||
|
||||
run: install
|
||||
$(MAKE) -C example
|
||||
$(MAKE) -C example run
|
||||
|
54
READMII
Normal file
54
READMII
Normal file
@ -0,0 +1,54 @@
|
||||
|
||||
INTRODUCTION
|
||||
============
|
||||
|
||||
The NTFS-3G driver is an open source, freely available read/write NTFS driver
|
||||
for Linux, FreeBSD, Mac OS X, NetBSD, Solaris and Haiku. It provides safe and
|
||||
fast handling of the Windows XP, Windows Server 2003, Windows 2000, Windows
|
||||
Vista, and Windows Server 2008 file systems.
|
||||
|
||||
The purpose of the project is to develop, continuously quality test and
|
||||
support a trustable, featureful and high performance solution for hardware
|
||||
platforms and operating systems whose users need to reliably interoperate
|
||||
with NTFS. Besides this practical goal, the project also aims to explore
|
||||
the limits of the hybrid, kernel/user space filesystem driver approach,
|
||||
performance, reliability and feature richness per invested effort wise.
|
||||
|
||||
The driver is in STABLE status. The test methods, the test suites used
|
||||
can be found at
|
||||
|
||||
http://ntfs-3g.org/quality.html
|
||||
|
||||
News, support answers, problem submission instructions, support and discussion
|
||||
forums, performance numbers and other information are available on the project
|
||||
web site at
|
||||
|
||||
http://ntfs-3g.org
|
||||
|
||||
For more details on the NTFS-3G project see the 'original' folder included
|
||||
with this package.
|
||||
|
||||
COMPILING AND INSTALLATION
|
||||
==========================
|
||||
|
||||
Make sure you have devKitPPC and the latest libogc installed. Then type:
|
||||
|
||||
make
|
||||
make install # or 'sudo make install' if you aren't root.
|
||||
|
||||
|
||||
USAGE
|
||||
=====
|
||||
|
||||
NTFS related routines can be accessed by adding the following line to your
|
||||
source file(s).
|
||||
|
||||
#include <ntfs.h>
|
||||
|
||||
When compiling you must also link against the libntfs. To do this add -lntfs
|
||||
to the LIBS section of your application Makefile. For example:
|
||||
|
||||
LIBS := -lwiiuse -lbte -lntfs -lfat -logc -lm
|
||||
|
||||
For a more practical example of using NTFS in your application,
|
||||
see the included 'example' folder.
|
139
example/Makefile
Normal file
139
example/Makefile
Normal file
@ -0,0 +1,139 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
# Clear the implicit built in rules
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(DEVKITPPC)),)
|
||||
$(error "Please set DEVKITPPC in your environment. export DEVKITPPC=<path to>devkitPPC")
|
||||
endif
|
||||
|
||||
include $(DEVKITPPC)/wii_rules
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# TARGET is the name of the output
|
||||
# BUILD is the directory where object files & intermediate files will be placed
|
||||
# SOURCES is a list of directories containing source code
|
||||
# INCLUDES is a list of directories containing extra header files
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET := $(notdir $(CURDIR))
|
||||
BUILD := build
|
||||
SOURCES := source
|
||||
DATA := data
|
||||
INCLUDES :=
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
CFLAGS = -g -O2 -Wall $(MACHDEP) $(INCLUDE)
|
||||
CXXFLAGS = $(CFLAGS)
|
||||
|
||||
LDFLAGS = -g $(MACHDEP) -Wl,-Map,$(notdir $@).map
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# any extra libraries we wish to link with the project
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBS := -lwiiuse -lbte -lntfs -lfat -logc -lm
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS :=
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# automatically build a list of object files for our project
|
||||
#---------------------------------------------------------------------------------
|
||||
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S)))
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
export LD := $(CC)
|
||||
else
|
||||
export LD := $(CXX)
|
||||
endif
|
||||
|
||||
export OFILES := $(addsuffix .o,$(BINFILES)) \
|
||||
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \
|
||||
$(sFILES:.s=.o) $(SFILES:.S=.o)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# build a list of include paths
|
||||
#---------------------------------------------------------------------------------
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES), -iquote $(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD) \
|
||||
-I$(LIBOGC_INC)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# build a list of library paths
|
||||
#---------------------------------------------------------------------------------
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \
|
||||
-L$(LIBOGC_LIB)
|
||||
|
||||
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||
.PHONY: $(BUILD) clean
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
$(BUILD):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).dol
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
run:
|
||||
wiiload $(TARGET).dol
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
$(OUTPUT).dol: $(OUTPUT).elf
|
||||
$(OUTPUT).elf: $(OFILES)
|
||||
|
||||
%.elf.o : %.elf
|
||||
@echo $(notdir $<)
|
||||
$(bin2o)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# This rule links in binary data with the .jpg extension
|
||||
#---------------------------------------------------------------------------------
|
||||
%.jpg.o : %.jpg
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
$(bin2o)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
229
example/source/main.c
Normal file
229
example/source/main.c
Normal file
@ -0,0 +1,229 @@
|
||||
/**
|
||||
* main.c - Directory listing example for NTFS-based devices.
|
||||
*
|
||||
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <gctypes.h>
|
||||
#include <gccore.h>
|
||||
#include <wiiuse/wpad.h>
|
||||
|
||||
#include <fat.h>
|
||||
#include <ntfs.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
|
||||
static void *xfb = NULL;
|
||||
static GXRModeObj *rmode = NULL;
|
||||
|
||||
void list(const char *path, int depth)
|
||||
{
|
||||
DIR *pdir;
|
||||
struct dirent *pent;
|
||||
struct stat st;
|
||||
char indent[PATH_MAX] = {0};
|
||||
char new_path[PATH_MAX] = {0};
|
||||
|
||||
// Open the directory
|
||||
pdir = opendir(path);
|
||||
if (pdir) {
|
||||
|
||||
// Make this our current directory
|
||||
chdir(path);
|
||||
|
||||
// Build a directory indent (for better readability)
|
||||
memset(indent, ' ', depth * 2);
|
||||
|
||||
// List the contents of the directory
|
||||
while ((pent = readdir(pdir)) != NULL) {
|
||||
if ((strcmp(pent->d_name, ".") == 0) || (strcmp(pent->d_name, "..") == 0))
|
||||
continue;
|
||||
|
||||
// Get the entries stats
|
||||
if (stat(pent->d_name, &st) == -1)
|
||||
continue;
|
||||
|
||||
// List the entry
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
printf(" D %s%s/\n", indent, pent->d_name);
|
||||
|
||||
// List the directories contents
|
||||
sprintf(new_path, "%s/%s", path, pent->d_name);
|
||||
list(new_path, depth + 1);
|
||||
chdir(path);
|
||||
|
||||
} else if (S_ISREG(st.st_mode)) {
|
||||
printf(" F %s%s (%lu)\n", indent, pent->d_name, (unsigned long int)st.st_size);
|
||||
} else {
|
||||
printf(" ? %s%s\n", indent, pent->d_name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Close the directory
|
||||
closedir(pdir);
|
||||
|
||||
} else {
|
||||
printf("opendir(%s) failure.\n", path);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------
|
||||
int main(int argc, char **argv) {
|
||||
//---------------------------------------------------------------------------------
|
||||
|
||||
// Initialise the video system
|
||||
VIDEO_Init();
|
||||
|
||||
// This function initialises the attached controllers
|
||||
WPAD_Init();
|
||||
|
||||
// Obtain the preferred video mode from the system
|
||||
// This will correspond to the settings in the Wii menu
|
||||
rmode = VIDEO_GetPreferredMode(NULL);
|
||||
|
||||
// Allocate memory for the display in the uncached region
|
||||
xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
|
||||
|
||||
// Initialise the console, required for printf
|
||||
console_init(xfb, 20, 20, rmode->fbWidth, rmode->xfbHeight, rmode->fbWidth * VI_DISPLAY_PIX_SZ);
|
||||
|
||||
// Set up the video registers with the chosen mode
|
||||
VIDEO_Configure(rmode);
|
||||
|
||||
// Tell the video hardware where our display memory is
|
||||
VIDEO_SetNextFramebuffer(xfb);
|
||||
|
||||
// Make the display visible
|
||||
VIDEO_SetBlack(FALSE);
|
||||
|
||||
// Flush the video register changes to the hardware
|
||||
VIDEO_Flush();
|
||||
|
||||
// Wait for Video setup to complete
|
||||
VIDEO_WaitVSync();
|
||||
if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync();
|
||||
|
||||
|
||||
// The console understands VT terminal escape codes
|
||||
// This positions the cursor on row 2, column 0
|
||||
// we can use variables for this with format codes too
|
||||
// e.g. printf ("\x1b[%d;%dH", row, column );
|
||||
printf("\x1b[2;0H");
|
||||
|
||||
printf("\n");
|
||||
printf(" NTFS Directory Listing Example\n");
|
||||
printf("\n");
|
||||
printf(" - 'LEFT' and 'RIGHT' to select a volume\n");
|
||||
printf(" - 'A' to enumerate the selected volume\n");
|
||||
printf(" - 'HOME' to quit\n");
|
||||
printf("\n");
|
||||
|
||||
bool listed = false;
|
||||
ntfs_md *mounts = NULL;
|
||||
int mountCount = 0;
|
||||
int mountIndex = 0;
|
||||
int i;
|
||||
|
||||
// Mount FAT devices
|
||||
fatInitDefault();
|
||||
|
||||
// Mount all NTFS volumes on all inserted block devices
|
||||
mountCount = ntfsMountAll(&mounts, NTFS_DEFAULT | NTFS_RECOVER);
|
||||
if (mountCount == -1)
|
||||
printf("Error whilst mounting devices (%i).\n", errno);
|
||||
else if (mountCount == 0)
|
||||
printf("No NTFS volumes were found and/or mounted.\n");
|
||||
else
|
||||
printf("%i NTFS volumes(s) mounted!\n\n", mountCount);
|
||||
|
||||
// List all mounted NTFS volumes
|
||||
for (i = 0; i < mountCount; i++)
|
||||
printf("%i - %s:/ (%s)\n", i + 1, mounts[i].name, ntfsGetVolumeName(mounts[i].name));
|
||||
|
||||
printf("\n");
|
||||
while (1) {
|
||||
|
||||
// Call WPAD_ScanPads each loop, this reads the latest controller states
|
||||
WPAD_ScanPads();
|
||||
|
||||
// WPAD_ButtonsDown tells us which buttons were pressed in this loop
|
||||
// this is a "one shot" state which will not fire again until the button has been released
|
||||
u32 pressed = WPAD_ButtonsDown(0);
|
||||
|
||||
// Break from main loop
|
||||
if (pressed & WPAD_BUTTON_HOME) break;
|
||||
|
||||
// If there is at least one volume mounted and we have not yet listed one...
|
||||
if (mountCount > 0 && !listed) {
|
||||
|
||||
// Deincrement the selected volumes index
|
||||
if (pressed & WPAD_BUTTON_LEFT)
|
||||
mountIndex = MIN(MAX(mountIndex - 1, 0), mountCount - 1);
|
||||
|
||||
// Increment the selected volumes index
|
||||
if (pressed & WPAD_BUTTON_RIGHT)
|
||||
mountIndex = MIN(MAX(mountIndex + 1, 0), mountCount - 1);
|
||||
|
||||
// Enumerate the selected volumes contents
|
||||
if (pressed & WPAD_BUTTON_A) {
|
||||
printf("\n\n");
|
||||
|
||||
// List the volumes root directory
|
||||
char path[PATH_MAX] = {0};
|
||||
strcpy(path, mounts[mountIndex].name);
|
||||
strcat(path, ":/");
|
||||
list(path, 0);
|
||||
listed = true;
|
||||
|
||||
printf("\n");
|
||||
printf("Press 'HOME' to quit.\n\n");
|
||||
|
||||
}
|
||||
|
||||
// If we have not listed a volume yet then prompt to select one
|
||||
if(!listed) {
|
||||
printf("\rSelect a NTFS volume: < %i >", mountIndex + 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Wait for the next frame
|
||||
VIDEO_WaitVSync();
|
||||
|
||||
}
|
||||
|
||||
// Unmount all NTFS volumes and clean up
|
||||
if (mounts) {
|
||||
for (i = 0; i < mountCount; i++)
|
||||
ntfsUnmount(mounts[i].name, true);
|
||||
free(mounts);
|
||||
}
|
||||
|
||||
// We return to the launcher application via exit
|
||||
exit(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
148
include/ntfs.h
Normal file
148
include/ntfs.h
Normal file
@ -0,0 +1,148 @@
|
||||
/**
|
||||
* ntfs.h - Simple functionality for startup, mounting and unmounting of NTFS-based devices.
|
||||
*
|
||||
* Copyright (c) 2010 Dimok
|
||||
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
||||
* Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _LIBNTFS_H
|
||||
#define _LIBNTFS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gctypes.h>
|
||||
#include <gccore.h>
|
||||
#include <ogc/disc_io.h>
|
||||
|
||||
/* NTFS errno values */
|
||||
#define ENOPART 3000 /* No partition was found */
|
||||
#define EINVALPART 3001 /* Specified partition is invalid or not supported */
|
||||
#define EDIRTY 3002 /* Volume is dirty and NTFS_RECOVER was not specified during mount */
|
||||
#define EHIBERNATED 3003 /* Volume is hibernated and NTFS_IGNORE_HIBERFILE was not specified during mount */
|
||||
|
||||
/* NTFS cache options */
|
||||
#define CACHE_DEFAULT_PAGE_COUNT 8 /* The default number of pages in the cache */
|
||||
#define CACHE_DEFAULT_PAGE_SIZE 128 /* The default number of sectors per cache page */
|
||||
|
||||
/* NTFS mount flags */
|
||||
#define NTFS_DEFAULT 0x00000000 /* Standard mount, expects a clean, non-hibernated volume */
|
||||
#define NTFS_SHOW_HIDDEN_FILES 0x00000001 /* Display hidden files when enumerating directories */
|
||||
#define NTFS_SHOW_SYSTEM_FILES 0x00000002 /* Display system files when enumerating directories */
|
||||
#define NTFS_UPDATE_ACCESS_TIMES 0x00000004 /* Update file and directory access times */
|
||||
#define NTFS_RECOVER 0x00000008 /* Reset $LogFile if dirty (i.e. from unclean disconnect) */
|
||||
#define NTFS_IGNORE_HIBERFILE 0x00000010 /* Mount even if volume is hibernated */
|
||||
#define NTFS_READ_ONLY 0x00000020 /* Mount in read only mode */
|
||||
#define NTFS_IGNORE_CASE 0x00000040 /* Ignore case sensitivity. Everything must be and will be provided in lowercase. */
|
||||
#define NTFS_SU NTFS_SHOW_HIDDEN_FILES | NTFS_SHOW_SYSTEM_FILES
|
||||
#define NTFS_FORCE NTFS_RECOVER | NTFS_IGNORE_HIBERFILE
|
||||
|
||||
/**
|
||||
* ntfs_md - NTFS mount descriptor
|
||||
*/
|
||||
typedef struct _ntfs_md {
|
||||
char name[32]; /* Mount name (can be accessed as "name:/") */
|
||||
const DISC_INTERFACE *interface; /* Block device containing the mounted partition */
|
||||
sec_t startSector; /* Local block address to first sector of partition */
|
||||
} ntfs_md;
|
||||
|
||||
/**
|
||||
* Find all NTFS partitions on a block device.
|
||||
*
|
||||
* @param INTERFACE The block device to search
|
||||
* @param PARTITIONS (out) A pointer to receive the array of partition start sectors
|
||||
*
|
||||
* @return The number of entries in PARTITIONS or -1 if an error occurred (see errno)
|
||||
* @note The caller is responsible for freeing PARTITIONS when finished with it
|
||||
*/
|
||||
extern int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions);
|
||||
|
||||
/**
|
||||
* Mount all NTFS partitions on all inserted block devices.
|
||||
*
|
||||
* @param MOUNTS (out) A pointer to receive the array of mount descriptors
|
||||
* @param FLAGS Additional mounting flags. (see above)
|
||||
*
|
||||
* @return The number of entries in MOUNTS or -1 if an error occurred (see errno)
|
||||
* @note The caller is responsible for freeing MOUNTS when finished with it
|
||||
* @note All device caches are setup using default values (see above)
|
||||
*/
|
||||
extern int ntfsMountAll (ntfs_md **mounts, u32 flags);
|
||||
|
||||
/**
|
||||
* Mount all NTFS partitions on a block devices.
|
||||
*
|
||||
* @param INTERFACE The block device to mount.
|
||||
* @param MOUNTS (out) A pointer to receive the array of mount descriptors
|
||||
* @param FLAGS Additional mounting flags. (see above)
|
||||
*
|
||||
* @return The number of entries in MOUNTS or -1 if an error occurred (see errno)
|
||||
* @note The caller is responsible for freeing MOUNTS when finished with it
|
||||
* @note The device cache is setup using default values (see above)
|
||||
*/
|
||||
extern int ntfsMountDevice (const DISC_INTERFACE* interface, ntfs_md **mounts, u32 flags);
|
||||
|
||||
/**
|
||||
* Mount a NTFS partition from a specific sector on a block device.
|
||||
*
|
||||
* @param NAME The name to mount the device under (can then be accessed as "NAME:/")
|
||||
* @param INTERFACE The block device to mount
|
||||
* @param STARTSECTOR The sector the partition begins at (see @ntfsFindPartitions)
|
||||
* @param CACHEPAGECOUNT The total number of pages in the device cache
|
||||
* @param CACHEPAGESIZE The number of sectors per cache page
|
||||
* @param FLAGS Additional mounting flags (see above)
|
||||
*
|
||||
* @return True if mount was successful, false if no partition was found or an error occurred (see errno)
|
||||
* @note ntfsFindPartitions should be used first to locate the partitions start sector
|
||||
*/
|
||||
extern bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSector, u32 cachePageCount, u32 cachePageSize, u32 flags);
|
||||
|
||||
/**
|
||||
* Unmount a NTFS partition.
|
||||
*
|
||||
* @param NAME The name of mount used in ntfsMountSimple() and ntfsMount()
|
||||
* @param FORCE If true unmount even if the device is busy (may lead to data lose)
|
||||
*/
|
||||
extern void ntfsUnmount (const char *name, bool force);
|
||||
|
||||
/**
|
||||
* Get the volume name of a mounted NTFS partition.
|
||||
*
|
||||
* @param NAME The name of mount (see @ntfsMountAll, @ntfsMountDevice, and @ntfsMount)
|
||||
*
|
||||
* @return The volumes name if successful or NULL if an error occurred (see errno)
|
||||
*/
|
||||
extern const char *ntfsGetVolumeName (const char *name);
|
||||
|
||||
/**
|
||||
* Set the volume name of a mounted NTFS partition.
|
||||
*
|
||||
* @param NAME The name of mount (see @ntfsMountAll, @ntfsMountDevice, and @ntfsMount)
|
||||
* @param VOLUMENAME The new volume name
|
||||
*
|
||||
* @return True if mount was successful, false if an error occurred (see errno)
|
||||
* @note The mount must be write-enabled else this will fail
|
||||
*/
|
||||
extern bool ntfsSetVolumeName (const char *name, const char *volumeName);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LIBNTFS_H */
|
130
source/Makefile
Normal file
130
source/Makefile
Normal file
@ -0,0 +1,130 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
# Clear the implicit built in rules
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(DEVKITPPC)),)
|
||||
$(error "Please set DEVKITPPC in your environment. export DEVKITPPC=<path to>devkitPPC")
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM),wii)
|
||||
include $(DEVKITPPC)/wii_rules
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM),cube)
|
||||
include $(DEVKITPPC)/gamecube_rules
|
||||
endif
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# BUILD is the directory where object files & intermediate files will be placed
|
||||
# SOURCES is a list of directories containing source code
|
||||
# INCLUDES is a list of directories containing extra header files
|
||||
#---------------------------------------------------------------------------------
|
||||
BUILD ?= wii_release
|
||||
SOURCES := .
|
||||
INCLUDES := ../include
|
||||
LIBDIR := ../lib
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
CFLAGS = -Os -Wall -ffast-math -pipe $(MACHDEP) $(INCLUDE) -DHAVE_CONFIG_H
|
||||
CXXFLAGS = $(CFLAGS)
|
||||
ASFLAGS := -g
|
||||
export NTFSBIN := $(LIBDIR)/$(PLATFORM)/libntfs.a
|
||||
|
||||
ifeq ($(BUILD),cube_debug)
|
||||
CFLAGS += -DDEBUG
|
||||
CXXFLAGS += -DDEBUG
|
||||
endif
|
||||
ifeq ($(BUILD),wii_debug)
|
||||
CFLAGS += -DDEBUG
|
||||
CXXFLAGS += -DDEBUG
|
||||
endif
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# any extra libraries we wish to link with the project
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBS :=
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS :=
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
|
||||
export OFILES := $(addsuffix .o,$(BINFILES)) \
|
||||
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD) \
|
||||
-I$(LIBOGC_INC)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \
|
||||
-L$(LIBOGC_LIB)
|
||||
|
||||
.PHONY: $(BUILD) clean
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
$(BUILD):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr wii_debug wii_release cube_debug cube_release $(LIBDIR)
|
||||
|
||||
all: $(NTFSBIN)
|
||||
|
||||
install:
|
||||
cp ../include/ntfs.h $(DEVKITPRO)/libogc/include
|
||||
cp ../lib/wii/libntfs.a $(DEVKITPRO)/libogc/lib/wii
|
||||
cp ../lib/cube/libntfs.a $(DEVKITPRO)/libogc/lib/cube
|
||||
|
||||
wii-install:
|
||||
cp ../include/ntfs.h $(DEVKITPRO)/libogc/include
|
||||
cp ../lib/wii/libntfs.a $(DEVKITPRO)/libogc/lib/wii
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
$(NTFSBIN): $(OFILES) $(LIBDIR)/$(PLATFORM)
|
||||
@rm -f "../$(NTFSBIN)"
|
||||
@$(AR) rcs "../$(NTFSBIN)" $(OFILES)
|
||||
@echo built ... $(notdir $@)
|
||||
|
||||
$(LIBDIR)/$(PLATFORM):
|
||||
mkdir -p ../$(LIBDIR)/$(PLATFORM)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
||||
|
4293
source/acls.c
Normal file
4293
source/acls.c
Normal file
File diff suppressed because it is too large
Load Diff
199
source/acls.h
Normal file
199
source/acls.h
Normal file
@ -0,0 +1,199 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2007-2008 Jean-Pierre Andre
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef ACLS_H
|
||||
#define ACLS_H
|
||||
|
||||
/*
|
||||
* JPA configuration modes for security.c / acls.c
|
||||
* should be moved to some config file
|
||||
*/
|
||||
|
||||
#define BUFSZ 1024 /* buffer size to read mapping file */
|
||||
#define MAPPINGFILE ".NTFS-3G/UserMapping" /* default mapping file */
|
||||
#define LINESZ 120 /* maximum useful size of a mapping line */
|
||||
#define CACHE_PERMISSIONS_BITS 6 /* log2 of unitary allocation of permissions */
|
||||
#define CACHE_PERMISSIONS_SIZE 262144 /* max cacheable permissions */
|
||||
|
||||
/*
|
||||
* JPA The following must be in some library...
|
||||
* but did not found out where
|
||||
*/
|
||||
|
||||
#define endian_rev16(x) (((x >> 8) & 255) | ((x & 255) << 8))
|
||||
#define endian_rev32(x) (((x >> 24) & 255) | ((x >> 8) & 0xff00) \
|
||||
| ((x & 0xff00) << 8) | ((x & 255) << 24))
|
||||
|
||||
#define cpu_to_be16(x) endian_rev16(cpu_to_le16(x))
|
||||
#define cpu_to_be32(x) endian_rev32(cpu_to_le32(x))
|
||||
|
||||
/*
|
||||
* Macro definitions needed to share code with secaudit
|
||||
*/
|
||||
|
||||
#define NTFS_FIND_USID(map,uid,buf) ntfs_find_usid(map,uid,buf)
|
||||
#define NTFS_FIND_GSID(map,gid,buf) ntfs_find_gsid(map,gid,buf)
|
||||
#define NTFS_FIND_USER(map,usid) ntfs_find_user(map,usid)
|
||||
#define NTFS_FIND_GROUP(map,gsid) ntfs_find_group(map,gsid)
|
||||
|
||||
|
||||
/*
|
||||
* Matching of ntfs permissions to Linux permissions
|
||||
* these constants are adapted to endianness
|
||||
* when setting, set them all
|
||||
* when checking, check one is present
|
||||
*/
|
||||
|
||||
/* flags which are set to mean exec, write or read */
|
||||
|
||||
#define FILE_READ (FILE_READ_DATA)
|
||||
#define FILE_WRITE (FILE_WRITE_DATA | FILE_APPEND_DATA \
|
||||
| READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA)
|
||||
#define FILE_EXEC (FILE_EXECUTE)
|
||||
#define DIR_READ FILE_LIST_DIRECTORY
|
||||
#define DIR_WRITE (FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY | FILE_DELETE_CHILD \
|
||||
| READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA)
|
||||
#define DIR_EXEC (FILE_TRAVERSE)
|
||||
|
||||
/* flags tested for meaning exec, write or read */
|
||||
/* tests for write allow for interpretation of a sticky bit */
|
||||
|
||||
#define FILE_GREAD (FILE_READ_DATA | GENERIC_READ)
|
||||
#define FILE_GWRITE (FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE)
|
||||
#define FILE_GEXEC (FILE_EXECUTE | GENERIC_EXECUTE)
|
||||
#define DIR_GREAD (FILE_LIST_DIRECTORY | GENERIC_READ)
|
||||
#define DIR_GWRITE (FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY | GENERIC_WRITE)
|
||||
#define DIR_GEXEC (FILE_TRAVERSE | GENERIC_EXECUTE)
|
||||
|
||||
/* standard owner (and administrator) rights */
|
||||
|
||||
#define OWNER_RIGHTS (DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER \
|
||||
| SYNCHRONIZE \
|
||||
| FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES \
|
||||
| FILE_READ_EA | FILE_WRITE_EA)
|
||||
|
||||
/* standard world rights */
|
||||
|
||||
#define WORLD_RIGHTS (READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA \
|
||||
| SYNCHRONIZE)
|
||||
|
||||
/* inheritance flags for files and directories */
|
||||
|
||||
#define FILE_INHERITANCE NO_PROPAGATE_INHERIT_ACE
|
||||
#define DIR_INHERITANCE (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE)
|
||||
|
||||
/*
|
||||
* To identify NTFS ACL meaning Posix ACL granted to root
|
||||
* we use rights always granted to anybody, so they have no impact
|
||||
* either on Windows or on Linux.
|
||||
*/
|
||||
|
||||
#define ROOT_OWNER_UNMARK SYNCHRONIZE /* ACL granted to root as owner */
|
||||
#define ROOT_GROUP_UNMARK FILE_READ_EA /* ACL granted to root as group */
|
||||
|
||||
/*
|
||||
* A type large enough to hold any SID
|
||||
*/
|
||||
|
||||
typedef char BIGSID[40];
|
||||
|
||||
/*
|
||||
* Struct to hold the input mapping file
|
||||
* (private to this module)
|
||||
*/
|
||||
|
||||
struct MAPLIST {
|
||||
struct MAPLIST *next;
|
||||
char *uidstr; /* uid text from the same record */
|
||||
char *gidstr; /* gid text from the same record */
|
||||
char *sidstr; /* sid text from the same record */
|
||||
char maptext[LINESZ + 1];
|
||||
};
|
||||
|
||||
typedef int (*FILEREADER)(void *fileid, char *buf, size_t size, off_t pos);
|
||||
|
||||
/*
|
||||
* Constants defined in acls.c
|
||||
*/
|
||||
|
||||
extern const SID *adminsid;
|
||||
extern const SID *worldsid;
|
||||
|
||||
/*
|
||||
* Functions defined in acls.c
|
||||
*/
|
||||
|
||||
BOOL ntfs_valid_descr(const char *securattr, unsigned int attrsz);
|
||||
BOOL ntfs_valid_pattern(const SID *sid);
|
||||
BOOL ntfs_valid_sid(const SID *sid);
|
||||
BOOL ntfs_same_sid(const SID *first, const SID *second);
|
||||
|
||||
BOOL ntfs_is_user_sid(const SID *usid);
|
||||
|
||||
|
||||
int ntfs_sid_size(const SID * sid);
|
||||
unsigned int ntfs_attr_size(const char *attr);
|
||||
|
||||
const SID *ntfs_find_usid(const struct MAPPING *usermapping,
|
||||
uid_t uid, SID *pdefsid);
|
||||
const SID *ntfs_find_gsid(const struct MAPPING *groupmapping,
|
||||
gid_t gid, SID *pdefsid);
|
||||
uid_t ntfs_find_user(const struct MAPPING *usermapping, const SID *usid);
|
||||
gid_t ntfs_find_group(const struct MAPPING *groupmapping, const SID * gsid);
|
||||
const SID *ntfs_acl_owner(const char *secattr);
|
||||
|
||||
#if POSIXACLS
|
||||
|
||||
BOOL ntfs_valid_posix(const struct POSIX_SECURITY *pxdesc);
|
||||
void ntfs_sort_posix(struct POSIX_SECURITY *pxdesc);
|
||||
int ntfs_merge_mode_posix(struct POSIX_SECURITY *pxdesc, mode_t mode);
|
||||
struct POSIX_SECURITY *ntfs_build_inherited_posix(
|
||||
const struct POSIX_SECURITY *pxdesc, mode_t mode,
|
||||
mode_t umask, BOOL isdir);
|
||||
struct POSIX_SECURITY *ntfs_replace_acl(const struct POSIX_SECURITY *oldpxdesc,
|
||||
const struct POSIX_ACL *newacl, int count, BOOL deflt);
|
||||
struct POSIX_SECURITY *ntfs_build_permissions_posix(
|
||||
struct MAPPING* const mapping[],
|
||||
const char *securattr,
|
||||
const SID *usid, const SID *gsid, BOOL isdir);
|
||||
struct POSIX_SECURITY *ntfs_merge_descr_posix(const struct POSIX_SECURITY *first,
|
||||
const struct POSIX_SECURITY *second);
|
||||
char *ntfs_build_descr_posix(struct MAPPING* const mapping[],
|
||||
struct POSIX_SECURITY *pxdesc,
|
||||
int isdir, const SID *usid, const SID *gsid);
|
||||
|
||||
#endif /* POSIXACLS */
|
||||
|
||||
int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl,
|
||||
const SID *usid, const SID *gsid, BOOL fordir);
|
||||
int ntfs_build_permissions(const char *securattr,
|
||||
const SID *usid, const SID *gsid, BOOL isdir);
|
||||
char *ntfs_build_descr(mode_t mode,
|
||||
int isdir, const SID * usid, const SID * gsid);
|
||||
struct MAPLIST *ntfs_read_mapping(FILEREADER reader, void *fileid);
|
||||
struct MAPPING *ntfs_do_user_mapping(struct MAPLIST *firstitem);
|
||||
struct MAPPING *ntfs_do_group_mapping(struct MAPLIST *firstitem);
|
||||
void ntfs_free_mapping(struct MAPPING *mapping[]);
|
||||
|
||||
#endif /* ACLS_H */
|
||||
|
6401
source/attrib.c
Normal file
6401
source/attrib.c
Normal file
File diff suppressed because it is too large
Load Diff
375
source/attrib.h
Normal file
375
source/attrib.h
Normal file
@ -0,0 +1,375 @@
|
||||
/*
|
||||
* attrib.h - Exports for attribute handling. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2004 Anton Altaparmakov
|
||||
* Copyright (c) 2004-2005 Yura Pakhuchiy
|
||||
* Copyright (c) 2006-2007 Szabolcs Szakacsits
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_ATTRIB_H
|
||||
#define _NTFS_ATTRIB_H
|
||||
|
||||
/* Forward declarations */
|
||||
typedef struct _ntfs_attr ntfs_attr;
|
||||
typedef struct _ntfs_attr_search_ctx ntfs_attr_search_ctx;
|
||||
|
||||
#include "types.h"
|
||||
#include "inode.h"
|
||||
#include "unistr.h"
|
||||
#include "runlist.h"
|
||||
#include "volume.h"
|
||||
#include "debug.h"
|
||||
#include "logging.h"
|
||||
|
||||
extern ntfschar AT_UNNAMED[];
|
||||
extern ntfschar STREAM_SDS[];
|
||||
|
||||
/* The little endian Unicode string $TXF_DATA as a global constant. */
|
||||
extern ntfschar TXF_DATA[10];
|
||||
|
||||
/**
|
||||
* enum ntfs_lcn_special_values - special return values for ntfs_*_vcn_to_lcn()
|
||||
*
|
||||
* Special return values for ntfs_rl_vcn_to_lcn() and ntfs_attr_vcn_to_lcn().
|
||||
*
|
||||
* TODO: Describe them.
|
||||
*/
|
||||
typedef enum {
|
||||
LCN_HOLE = -1, /* Keep this as highest value or die! */
|
||||
LCN_RL_NOT_MAPPED = -2,
|
||||
LCN_ENOENT = -3,
|
||||
LCN_EINVAL = -4,
|
||||
LCN_EIO = -5,
|
||||
} ntfs_lcn_special_values;
|
||||
|
||||
/**
|
||||
* struct ntfs_attr_search_ctx - search context used in attribute search functions
|
||||
* @mrec: buffer containing mft record to search
|
||||
* @attr: attribute record in @mrec where to begin/continue search
|
||||
* @is_first: if true lookup_attr() begins search with @attr, else after @attr
|
||||
*
|
||||
* Structure must be initialized to zero before the first call to one of the
|
||||
* attribute search functions. Initialize @mrec to point to the mft record to
|
||||
* search, and @attr to point to the first attribute within @mrec (not necessary
|
||||
* if calling the _first() functions), and set @is_first to TRUE (not necessary
|
||||
* if calling the _first() functions).
|
||||
*
|
||||
* If @is_first is TRUE, the search begins with @attr. If @is_first is FALSE,
|
||||
* the search begins after @attr. This is so that, after the first call to one
|
||||
* of the search attribute functions, we can call the function again, without
|
||||
* any modification of the search context, to automagically get the next
|
||||
* matching attribute.
|
||||
*/
|
||||
struct _ntfs_attr_search_ctx {
|
||||
MFT_RECORD *mrec;
|
||||
ATTR_RECORD *attr;
|
||||
BOOL is_first;
|
||||
ntfs_inode *ntfs_ino;
|
||||
ATTR_LIST_ENTRY *al_entry;
|
||||
ntfs_inode *base_ntfs_ino;
|
||||
MFT_RECORD *base_mrec;
|
||||
ATTR_RECORD *base_attr;
|
||||
};
|
||||
|
||||
extern void ntfs_attr_reinit_search_ctx(ntfs_attr_search_ctx *ctx);
|
||||
extern ntfs_attr_search_ctx *ntfs_attr_get_search_ctx(ntfs_inode *ni,
|
||||
MFT_RECORD *mrec);
|
||||
extern void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx);
|
||||
|
||||
extern int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name,
|
||||
const u32 name_len, const IGNORE_CASE_BOOL ic,
|
||||
const VCN lowest_vcn, const u8 *val, const u32 val_len,
|
||||
ntfs_attr_search_ctx *ctx);
|
||||
|
||||
extern int ntfs_attr_position(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx);
|
||||
|
||||
extern ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol,
|
||||
const ATTR_TYPES type);
|
||||
|
||||
/**
|
||||
* ntfs_attrs_walk - syntactic sugar for walking all attributes in an inode
|
||||
* @ctx: initialised attribute search context
|
||||
*
|
||||
* Syntactic sugar for walking attributes in an inode.
|
||||
*
|
||||
* Return 0 on success and -1 on error with errno set to the error code from
|
||||
* ntfs_attr_lookup().
|
||||
*
|
||||
* Example: When you want to enumerate all attributes in an open ntfs inode
|
||||
* @ni, you can simply do:
|
||||
*
|
||||
* int err;
|
||||
* ntfs_attr_search_ctx *ctx = ntfs_attr_get_search_ctx(ni, NULL);
|
||||
* if (!ctx)
|
||||
* // Error code is in errno. Handle this case.
|
||||
* while (!(err = ntfs_attrs_walk(ctx))) {
|
||||
* ATTR_RECORD *attr = ctx->attr;
|
||||
* // attr now contains the next attribute. Do whatever you want
|
||||
* // with it and then just continue with the while loop.
|
||||
* }
|
||||
* if (err && errno != ENOENT)
|
||||
* // Ooops. An error occurred! You should handle this case.
|
||||
* // Now finished with all attributes in the inode.
|
||||
*/
|
||||
static __inline__ int ntfs_attrs_walk(ntfs_attr_search_ctx *ctx)
|
||||
{
|
||||
return ntfs_attr_lookup(AT_UNUSED, NULL, 0, CASE_SENSITIVE, 0,
|
||||
NULL, 0, ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* struct ntfs_attr - ntfs in memory non-resident attribute structure
|
||||
* @rl: if not NULL, the decompressed runlist
|
||||
* @ni: base ntfs inode to which this attribute belongs
|
||||
* @type: attribute type
|
||||
* @name: Unicode name of the attribute
|
||||
* @name_len: length of @name in Unicode characters
|
||||
* @state: NTFS attribute specific flags describing this attribute
|
||||
* @allocated_size: copy from the attribute record
|
||||
* @data_size: copy from the attribute record
|
||||
* @initialized_size: copy from the attribute record
|
||||
* @compressed_size: copy from the attribute record
|
||||
* @compression_block_size: size of a compression block (cb)
|
||||
* @compression_block_size_bits: log2 of the size of a cb
|
||||
* @compression_block_clusters: number of clusters per cb
|
||||
*
|
||||
* This structure exists purely to provide a mechanism of caching the runlist
|
||||
* of an attribute. If you want to operate on a particular attribute extent,
|
||||
* you should not be using this structure at all. If you want to work with a
|
||||
* resident attribute, you should not be using this structure at all. As a
|
||||
* fail-safe check make sure to test NAttrNonResident() and if it is false, you
|
||||
* know you shouldn't be using this structure.
|
||||
*
|
||||
* If you want to work on a resident attribute or on a specific attribute
|
||||
* extent, you should use ntfs_lookup_attr() to retrieve the attribute (extent)
|
||||
* record, edit that, and then write back the mft record (or set the
|
||||
* corresponding ntfs inode dirty for delayed write back).
|
||||
*
|
||||
* @rl is the decompressed runlist of the attribute described by this
|
||||
* structure. Obviously this only makes sense if the attribute is not resident,
|
||||
* i.e. NAttrNonResident() is true. If the runlist hasn't been decompressed yet
|
||||
* @rl is NULL, so be prepared to cope with @rl == NULL.
|
||||
*
|
||||
* @ni is the base ntfs inode of the attribute described by this structure.
|
||||
*
|
||||
* @type is the attribute type (see layout.h for the definition of ATTR_TYPES),
|
||||
* @name and @name_len are the little endian Unicode name and the name length
|
||||
* in Unicode characters of the attribute, respectively.
|
||||
*
|
||||
* @state contains NTFS attribute specific flags describing this attribute
|
||||
* structure. See ntfs_attr_state_bits above.
|
||||
*/
|
||||
struct _ntfs_attr {
|
||||
runlist_element *rl;
|
||||
ntfs_inode *ni;
|
||||
ATTR_TYPES type;
|
||||
ATTR_FLAGS data_flags;
|
||||
ntfschar *name;
|
||||
u32 name_len;
|
||||
unsigned long state;
|
||||
s64 allocated_size;
|
||||
s64 data_size;
|
||||
s64 initialized_size;
|
||||
s64 compressed_size;
|
||||
u32 compression_block_size;
|
||||
u8 compression_block_size_bits;
|
||||
u8 compression_block_clusters;
|
||||
s8 unused_runs; /* pre-reserved entries available */
|
||||
};
|
||||
|
||||
/**
|
||||
* enum ntfs_attr_state_bits - bits for the state field in the ntfs_attr
|
||||
* structure
|
||||
*/
|
||||
typedef enum {
|
||||
NA_Initialized, /* 1: structure is initialized. */
|
||||
NA_NonResident, /* 1: Attribute is not resident. */
|
||||
NA_BeingNonResident, /* 1: Attribute is being made not resident. */
|
||||
NA_FullyMapped, /* 1: Attribute has been fully mapped */
|
||||
NA_ComprClosing, /* 1: Compressed attribute is being closed */
|
||||
} ntfs_attr_state_bits;
|
||||
|
||||
#define test_nattr_flag(na, flag) test_bit(NA_##flag, (na)->state)
|
||||
#define set_nattr_flag(na, flag) set_bit(NA_##flag, (na)->state)
|
||||
#define clear_nattr_flag(na, flag) clear_bit(NA_##flag, (na)->state)
|
||||
|
||||
#define NAttrInitialized(na) test_nattr_flag(na, Initialized)
|
||||
#define NAttrSetInitialized(na) set_nattr_flag(na, Initialized)
|
||||
#define NAttrClearInitialized(na) clear_nattr_flag(na, Initialized)
|
||||
|
||||
#define NAttrNonResident(na) test_nattr_flag(na, NonResident)
|
||||
#define NAttrSetNonResident(na) set_nattr_flag(na, NonResident)
|
||||
#define NAttrClearNonResident(na) clear_nattr_flag(na, NonResident)
|
||||
|
||||
#define NAttrBeingNonResident(na) test_nattr_flag(na, BeingNonResident)
|
||||
#define NAttrSetBeingNonResident(na) set_nattr_flag(na, BeingNonResident)
|
||||
#define NAttrClearBeingNonResident(na) clear_nattr_flag(na, BeingNonResident)
|
||||
|
||||
#define NAttrFullyMapped(na) test_nattr_flag(na, FullyMapped)
|
||||
#define NAttrSetFullyMapped(na) set_nattr_flag(na, FullyMapped)
|
||||
#define NAttrClearFullyMapped(na) clear_nattr_flag(na, FullyMapped)
|
||||
|
||||
#define NAttrComprClosing(na) test_nattr_flag(na, ComprClosing)
|
||||
#define NAttrSetComprClosing(na) set_nattr_flag(na, ComprClosing)
|
||||
#define NAttrClearComprClosing(na) clear_nattr_flag(na, ComprClosing)
|
||||
|
||||
#define GenNAttrIno(func_name, flag) \
|
||||
extern int NAttr##func_name(ntfs_attr *na); \
|
||||
extern void NAttrSet##func_name(ntfs_attr *na); \
|
||||
extern void NAttrClear##func_name(ntfs_attr *na);
|
||||
|
||||
GenNAttrIno(Compressed, FILE_ATTR_COMPRESSED)
|
||||
GenNAttrIno(Encrypted, FILE_ATTR_ENCRYPTED)
|
||||
GenNAttrIno(Sparse, FILE_ATTR_SPARSE_FILE)
|
||||
#undef GenNAttrIno
|
||||
|
||||
/**
|
||||
* union attr_val - Union of all known attribute values
|
||||
*
|
||||
* For convenience. Used in the attr structure.
|
||||
*/
|
||||
typedef union {
|
||||
u8 _default; /* Unnamed u8 to serve as default when just using
|
||||
a_val without specifying any of the below. */
|
||||
STANDARD_INFORMATION std_inf;
|
||||
ATTR_LIST_ENTRY al_entry;
|
||||
FILE_NAME_ATTR filename;
|
||||
OBJECT_ID_ATTR obj_id;
|
||||
SECURITY_DESCRIPTOR_ATTR sec_desc;
|
||||
VOLUME_NAME vol_name;
|
||||
VOLUME_INFORMATION vol_inf;
|
||||
DATA_ATTR data;
|
||||
INDEX_ROOT index_root;
|
||||
INDEX_BLOCK index_blk;
|
||||
BITMAP_ATTR bmp;
|
||||
REPARSE_POINT reparse;
|
||||
EA_INFORMATION ea_inf;
|
||||
EA_ATTR ea;
|
||||
PROPERTY_SET property_set;
|
||||
LOGGED_UTILITY_STREAM logged_util_stream;
|
||||
EFS_ATTR_HEADER efs;
|
||||
} attr_val;
|
||||
|
||||
extern void ntfs_attr_init(ntfs_attr *na, const BOOL non_resident,
|
||||
const ATTR_FLAGS data_flags, const BOOL encrypted,
|
||||
const BOOL sparse,
|
||||
const s64 allocated_size, const s64 data_size,
|
||||
const s64 initialized_size, const s64 compressed_size,
|
||||
const u8 compression_unit);
|
||||
|
||||
/* warning : in the following "name" has to be freeable */
|
||||
/* or one of constants AT_UNNAMED, NTFS_INDEX_I30 or STREAM_SDS */
|
||||
extern ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type,
|
||||
ntfschar *name, u32 name_len);
|
||||
extern void ntfs_attr_close(ntfs_attr *na);
|
||||
|
||||
extern s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count,
|
||||
void *b);
|
||||
extern s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count,
|
||||
const void *b);
|
||||
extern int ntfs_attr_pclose(ntfs_attr *na);
|
||||
|
||||
extern void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type,
|
||||
ntfschar *name, u32 name_len, s64 *data_size);
|
||||
|
||||
extern s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos,
|
||||
const s64 bk_cnt, const u32 bk_size, void *dst);
|
||||
extern s64 ntfs_attr_mst_pwrite(ntfs_attr *na, const s64 pos,
|
||||
s64 bk_cnt, const u32 bk_size, void *src);
|
||||
|
||||
extern int ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn);
|
||||
extern int ntfs_attr_map_whole_runlist(ntfs_attr *na);
|
||||
|
||||
extern LCN ntfs_attr_vcn_to_lcn(ntfs_attr *na, const VCN vcn);
|
||||
extern runlist_element *ntfs_attr_find_vcn(ntfs_attr *na, const VCN vcn);
|
||||
|
||||
extern int ntfs_attr_size_bounds_check(const ntfs_volume *vol,
|
||||
const ATTR_TYPES type, const s64 size);
|
||||
extern int ntfs_attr_can_be_resident(const ntfs_volume *vol,
|
||||
const ATTR_TYPES type);
|
||||
int ntfs_attr_make_non_resident(ntfs_attr *na,
|
||||
ntfs_attr_search_ctx *ctx);
|
||||
int ntfs_attr_force_non_resident(ntfs_attr *na);
|
||||
extern int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size);
|
||||
|
||||
extern int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type,
|
||||
ntfschar *name, u8 name_len, u8 *val, u32 size,
|
||||
ATTR_FLAGS flags);
|
||||
extern int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type,
|
||||
ntfschar *name, u8 name_len, VCN lowest_vcn, int dataruns_size,
|
||||
ATTR_FLAGS flags);
|
||||
extern int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx);
|
||||
|
||||
extern int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type,
|
||||
ntfschar *name, u8 name_len, u8 *val, s64 size);
|
||||
extern int ntfs_attr_set_flags(ntfs_inode *ni, ATTR_TYPES type,
|
||||
ntfschar *name, u8 name_len, ATTR_FLAGS flags, ATTR_FLAGS mask);
|
||||
extern int ntfs_attr_rm(ntfs_attr *na);
|
||||
|
||||
extern int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size);
|
||||
|
||||
extern int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a,
|
||||
const u32 new_size);
|
||||
|
||||
extern int ntfs_attr_record_move_to(ntfs_attr_search_ctx *ctx, ntfs_inode *ni);
|
||||
extern int ntfs_attr_record_move_away(ntfs_attr_search_ctx *ctx, int extra);
|
||||
|
||||
extern int ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn);
|
||||
|
||||
extern int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize);
|
||||
|
||||
/**
|
||||
* get_attribute_value_length - return the length of the value of an attribute
|
||||
* @a: pointer to a buffer containing the attribute record
|
||||
*
|
||||
* Return the byte size of the attribute value of the attribute @a (as it
|
||||
* would be after eventual decompression and filling in of holes if sparse).
|
||||
* If we return 0, check errno. If errno is 0 the actual length was 0,
|
||||
* otherwise errno describes the error.
|
||||
*
|
||||
* FIXME: Describe possible errnos.
|
||||
*/
|
||||
extern s64 ntfs_get_attribute_value_length(const ATTR_RECORD *a);
|
||||
|
||||
/**
|
||||
* get_attribute_value - return the attribute value of an attribute
|
||||
* @vol: volume on which the attribute is present
|
||||
* @a: attribute to get the value of
|
||||
* @b: destination buffer for the attribute value
|
||||
*
|
||||
* Make a copy of the attribute value of the attribute @a into the destination
|
||||
* buffer @b. Note, that the size of @b has to be at least equal to the value
|
||||
* returned by get_attribute_value_length(@a).
|
||||
*
|
||||
* Return number of bytes copied. If this is zero check errno. If errno is 0
|
||||
* then nothing was read due to a zero-length attribute value, otherwise
|
||||
* errno describes the error.
|
||||
*/
|
||||
extern s64 ntfs_get_attribute_value(const ntfs_volume *vol,
|
||||
const ATTR_RECORD *a, u8 *b);
|
||||
|
||||
extern void ntfs_attr_name_free(char **name);
|
||||
extern char *ntfs_attr_name_get(const ntfschar *uname, const int uname_len);
|
||||
extern int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type,
|
||||
ntfschar *name, u32 name_len);
|
||||
extern int ntfs_attr_remove(ntfs_inode *ni, const ATTR_TYPES type,
|
||||
ntfschar *name, u32 name_len);
|
||||
extern s64 ntfs_attr_get_free_bits(ntfs_attr *na);
|
||||
|
||||
#endif /* defined _NTFS_ATTRIB_H */
|
||||
|
314
source/attrlist.c
Normal file
314
source/attrlist.c
Normal file
@ -0,0 +1,314 @@
|
||||
/**
|
||||
* attrlist.c - Attribute list attribute handling code. Originated from the Linux-NTFS
|
||||
* project.
|
||||
*
|
||||
* Copyright (c) 2004-2005 Anton Altaparmakov
|
||||
* Copyright (c) 2004-2005 Yura Pakhuchiy
|
||||
* Copyright (c) 2006 Szabolcs Szakacsits
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "layout.h"
|
||||
#include "attrib.h"
|
||||
#include "attrlist.h"
|
||||
#include "debug.h"
|
||||
#include "unistr.h"
|
||||
#include "logging.h"
|
||||
#include "misc.h"
|
||||
|
||||
/**
|
||||
* ntfs_attrlist_need - check whether inode need attribute list
|
||||
* @ni: opened ntfs inode for which perform check
|
||||
*
|
||||
* Check whether all are attributes belong to one MFT record, in that case
|
||||
* attribute list is not needed.
|
||||
*
|
||||
* Return 1 if inode need attribute list, 0 if not, -1 on error with errno set
|
||||
* to the error code. If function succeed errno set to 0. The following error
|
||||
* codes are defined:
|
||||
* EINVAL - Invalid arguments passed to function or attribute haven't got
|
||||
* attribute list.
|
||||
*/
|
||||
int ntfs_attrlist_need(ntfs_inode *ni)
|
||||
{
|
||||
ATTR_LIST_ENTRY *ale;
|
||||
|
||||
if (!ni) {
|
||||
ntfs_log_trace("Invalid arguments.\n");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no);
|
||||
|
||||
if (!NInoAttrList(ni)) {
|
||||
ntfs_log_trace("Inode haven't got attribute list.\n");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ni->attr_list) {
|
||||
ntfs_log_trace("Corrupt in-memory struct.\n");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
ale = (ATTR_LIST_ENTRY *)ni->attr_list;
|
||||
while ((u8*)ale < ni->attr_list + ni->attr_list_size) {
|
||||
if (MREF_LE(ale->mft_reference) != ni->mft_no)
|
||||
return 1;
|
||||
ale = (ATTR_LIST_ENTRY *)((u8*)ale + le16_to_cpu(ale->length));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_attrlist_entry_add - add an attribute list attribute entry
|
||||
* @ni: opened ntfs inode, which contains that attribute
|
||||
* @attr: attribute record to add to attribute list
|
||||
*
|
||||
* Return 0 on success and -1 on error with errno set to the error code. The
|
||||
* following error codes are defined:
|
||||
* EINVAL - Invalid arguments passed to function.
|
||||
* ENOMEM - Not enough memory to allocate necessary buffers.
|
||||
* EIO - I/O error occurred or damaged filesystem.
|
||||
* EEXIST - Such attribute already present in attribute list.
|
||||
*/
|
||||
int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr)
|
||||
{
|
||||
ATTR_LIST_ENTRY *ale;
|
||||
MFT_REF mref;
|
||||
ntfs_attr *na = NULL;
|
||||
ntfs_attr_search_ctx *ctx;
|
||||
u8 *new_al;
|
||||
int entry_len, entry_offset, err;
|
||||
|
||||
ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n",
|
||||
(long long) ni->mft_no,
|
||||
(unsigned) le32_to_cpu(attr->type));
|
||||
|
||||
if (!ni || !attr) {
|
||||
ntfs_log_trace("Invalid arguments.\n");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
mref = MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number));
|
||||
|
||||
if (ni->nr_extents == -1)
|
||||
ni = ni->base_ni;
|
||||
|
||||
if (!NInoAttrList(ni)) {
|
||||
ntfs_log_trace("Attribute list isn't present.\n");
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Determine size and allocate memory for new attribute list. */
|
||||
entry_len = (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) *
|
||||
attr->name_length + 7) & ~7;
|
||||
new_al = ntfs_calloc(ni->attr_list_size + entry_len);
|
||||
if (!new_al)
|
||||
return -1;
|
||||
|
||||
/* Find place for the new entry. */
|
||||
ctx = ntfs_attr_get_search_ctx(ni, NULL);
|
||||
if (!ctx) {
|
||||
err = errno;
|
||||
goto err_out;
|
||||
}
|
||||
if (!ntfs_attr_lookup(attr->type, (attr->name_length) ? (ntfschar*)
|
||||
((u8*)attr + le16_to_cpu(attr->name_offset)) :
|
||||
AT_UNNAMED, attr->name_length, CASE_SENSITIVE,
|
||||
(attr->non_resident) ? le64_to_cpu(attr->lowest_vcn) :
|
||||
0, (attr->non_resident) ? NULL : ((u8*)attr +
|
||||
le16_to_cpu(attr->value_offset)), (attr->non_resident) ?
|
||||
0 : le32_to_cpu(attr->value_length), ctx)) {
|
||||
/* Found some extent, check it to be before new extent. */
|
||||
if (ctx->al_entry->lowest_vcn == attr->lowest_vcn) {
|
||||
err = EEXIST;
|
||||
ntfs_log_trace("Such attribute already present in the "
|
||||
"attribute list.\n");
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
goto err_out;
|
||||
}
|
||||
/* Add new entry after this extent. */
|
||||
ale = (ATTR_LIST_ENTRY*)((u8*)ctx->al_entry +
|
||||
le16_to_cpu(ctx->al_entry->length));
|
||||
} else {
|
||||
/* Check for real errors. */
|
||||
if (errno != ENOENT) {
|
||||
err = errno;
|
||||
ntfs_log_trace("Attribute lookup failed.\n");
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
goto err_out;
|
||||
}
|
||||
/* No previous extents found. */
|
||||
ale = ctx->al_entry;
|
||||
}
|
||||
/* Don't need it anymore, @ctx->al_entry points to @ni->attr_list. */
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
|
||||
/* Determine new entry offset. */
|
||||
entry_offset = ((u8 *)ale - ni->attr_list);
|
||||
/* Set pointer to new entry. */
|
||||
ale = (ATTR_LIST_ENTRY *)(new_al + entry_offset);
|
||||
/* Zero it to fix valgrind warning. */
|
||||
memset(ale, 0, entry_len);
|
||||
/* Form new entry. */
|
||||
ale->type = attr->type;
|
||||
ale->length = cpu_to_le16(entry_len);
|
||||
ale->name_length = attr->name_length;
|
||||
ale->name_offset = offsetof(ATTR_LIST_ENTRY, name);
|
||||
if (attr->non_resident)
|
||||
ale->lowest_vcn = attr->lowest_vcn;
|
||||
else
|
||||
ale->lowest_vcn = 0;
|
||||
ale->mft_reference = mref;
|
||||
ale->instance = attr->instance;
|
||||
memcpy(ale->name, (u8 *)attr + le16_to_cpu(attr->name_offset),
|
||||
attr->name_length * sizeof(ntfschar));
|
||||
|
||||
/* Resize $ATTRIBUTE_LIST to new length. */
|
||||
na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0);
|
||||
if (!na) {
|
||||
err = errno;
|
||||
ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n");
|
||||
goto err_out;
|
||||
}
|
||||
if (ntfs_attr_truncate(na, ni->attr_list_size + entry_len)) {
|
||||
err = errno;
|
||||
ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
/* Copy entries from old attribute list to new. */
|
||||
memcpy(new_al, ni->attr_list, entry_offset);
|
||||
memcpy(new_al + entry_offset + entry_len, ni->attr_list +
|
||||
entry_offset, ni->attr_list_size - entry_offset);
|
||||
|
||||
/* Set new runlist. */
|
||||
free(ni->attr_list);
|
||||
ni->attr_list = new_al;
|
||||
ni->attr_list_size = ni->attr_list_size + entry_len;
|
||||
NInoAttrListSetDirty(ni);
|
||||
/* Done! */
|
||||
ntfs_attr_close(na);
|
||||
return 0;
|
||||
err_out:
|
||||
if (na)
|
||||
ntfs_attr_close(na);
|
||||
free(new_al);
|
||||
errno = err;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_attrlist_entry_rm - remove an attribute list attribute entry
|
||||
* @ctx: attribute search context describing the attribute list entry
|
||||
*
|
||||
* Remove the attribute list entry @ctx->al_entry from the attribute list.
|
||||
*
|
||||
* Return 0 on success and -1 on error with errno set to the error code.
|
||||
*/
|
||||
int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx)
|
||||
{
|
||||
u8 *new_al;
|
||||
int new_al_len;
|
||||
ntfs_inode *base_ni;
|
||||
ntfs_attr *na;
|
||||
ATTR_LIST_ENTRY *ale;
|
||||
int err;
|
||||
|
||||
if (!ctx || !ctx->ntfs_ino || !ctx->al_entry) {
|
||||
ntfs_log_trace("Invalid arguments.\n");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ctx->base_ntfs_ino)
|
||||
base_ni = ctx->base_ntfs_ino;
|
||||
else
|
||||
base_ni = ctx->ntfs_ino;
|
||||
ale = ctx->al_entry;
|
||||
|
||||
ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld.\n",
|
||||
(long long) ctx->ntfs_ino->mft_no,
|
||||
(unsigned) le32_to_cpu(ctx->al_entry->type),
|
||||
(long long) le64_to_cpu(ctx->al_entry->lowest_vcn));
|
||||
|
||||
if (!NInoAttrList(base_ni)) {
|
||||
ntfs_log_trace("Attribute list isn't present.\n");
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Allocate memory for new attribute list. */
|
||||
new_al_len = base_ni->attr_list_size - le16_to_cpu(ale->length);
|
||||
new_al = ntfs_calloc(new_al_len);
|
||||
if (!new_al)
|
||||
return -1;
|
||||
|
||||
/* Reisze $ATTRIBUTE_LIST to new length. */
|
||||
na = ntfs_attr_open(base_ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0);
|
||||
if (!na) {
|
||||
err = errno;
|
||||
ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n");
|
||||
goto err_out;
|
||||
}
|
||||
if (ntfs_attr_truncate(na, new_al_len)) {
|
||||
err = errno;
|
||||
ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
/* Copy entries from old attribute list to new. */
|
||||
memcpy(new_al, base_ni->attr_list, (u8*)ale - base_ni->attr_list);
|
||||
memcpy(new_al + ((u8*)ale - base_ni->attr_list), (u8*)ale + le16_to_cpu(
|
||||
ale->length), new_al_len - ((u8*)ale - base_ni->attr_list));
|
||||
|
||||
/* Set new runlist. */
|
||||
free(base_ni->attr_list);
|
||||
base_ni->attr_list = new_al;
|
||||
base_ni->attr_list_size = new_al_len;
|
||||
NInoAttrListSetDirty(base_ni);
|
||||
/* Done! */
|
||||
ntfs_attr_close(na);
|
||||
return 0;
|
||||
err_out:
|
||||
if (na)
|
||||
ntfs_attr_close(na);
|
||||
free(new_al);
|
||||
errno = err;
|
||||
return -1;
|
||||
}
|
51
source/attrlist.h
Normal file
51
source/attrlist.h
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* attrlist.h - Exports for attribute list attribute handling.
|
||||
* Originated from Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2004 Anton Altaparmakov
|
||||
* Copyright (c) 2004 Yura Pakhuchiy
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_ATTRLIST_H
|
||||
#define _NTFS_ATTRLIST_H
|
||||
|
||||
#include "attrib.h"
|
||||
|
||||
extern int ntfs_attrlist_need(ntfs_inode *ni);
|
||||
|
||||
extern int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr);
|
||||
extern int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx);
|
||||
|
||||
/**
|
||||
* ntfs_attrlist_mark_dirty - set the attribute list dirty
|
||||
* @ni: ntfs inode which base inode contain dirty attribute list
|
||||
*
|
||||
* Set the attribute list dirty so it is written out later (at the latest at
|
||||
* ntfs_inode_close() time).
|
||||
*
|
||||
* This function cannot fail.
|
||||
*/
|
||||
static __inline__ void ntfs_attrlist_mark_dirty(ntfs_inode *ni)
|
||||
{
|
||||
if (ni->nr_extents == -1)
|
||||
NInoAttrListSetDirty(ni->base_ni);
|
||||
else
|
||||
NInoAttrListSetDirty(ni);
|
||||
}
|
||||
|
||||
#endif /* defined _NTFS_ATTRLIST_H */
|
57
source/bit_ops.h
Normal file
57
source/bit_ops.h
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
bit_ops.h
|
||||
Functions for dealing with conversion of data between types
|
||||
|
||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _BIT_OPS_H
|
||||
#define _BIT_OPS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/*-----------------------------------------------------------------
|
||||
Functions to deal with little endian values stored in uint8_t arrays
|
||||
-----------------------------------------------------------------*/
|
||||
static inline uint16_t u8array_to_u16 (const uint8_t* item, int offset) {
|
||||
return ( item[offset] | (item[offset + 1] << 8));
|
||||
}
|
||||
|
||||
static inline uint32_t u8array_to_u32 (const uint8_t* item, int offset) {
|
||||
return ( item[offset] | (item[offset + 1] << 8) | (item[offset + 2] << 16) | (item[offset + 3] << 24));
|
||||
}
|
||||
|
||||
static inline void u16_to_u8array (uint8_t* item, int offset, uint16_t value) {
|
||||
item[offset] = (uint8_t) value;
|
||||
item[offset + 1] = (uint8_t)(value >> 8);
|
||||
}
|
||||
|
||||
static inline void u32_to_u8array (uint8_t* item, int offset, uint32_t value) {
|
||||
item[offset] = (uint8_t) value;
|
||||
item[offset + 1] = (uint8_t)(value >> 8);
|
||||
item[offset + 2] = (uint8_t)(value >> 16);
|
||||
item[offset + 3] = (uint8_t)(value >> 24);
|
||||
}
|
||||
|
||||
#endif // _BIT_OPS_H
|
300
source/bitmap.c
Normal file
300
source/bitmap.c
Normal file
@ -0,0 +1,300 @@
|
||||
/**
|
||||
* bitmap.c - Bitmap handling code. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2002-2006 Anton Altaparmakov
|
||||
* Copyright (c) 2004-2005 Richard Russon
|
||||
* Copyright (c) 2004-2008 Szabolcs Szakacsits
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "attrib.h"
|
||||
#include "bitmap.h"
|
||||
#include "debug.h"
|
||||
#include "logging.h"
|
||||
#include "misc.h"
|
||||
|
||||
/**
|
||||
* ntfs_bit_set - set a bit in a field of bits
|
||||
* @bitmap: field of bits
|
||||
* @bit: bit to set
|
||||
* @new_value: value to set bit to (0 or 1)
|
||||
*
|
||||
* Set the bit @bit in the @bitmap to @new_value. Ignore all errors.
|
||||
*/
|
||||
void ntfs_bit_set(u8 *bitmap, const u64 bit, const u8 new_value)
|
||||
{
|
||||
if (!bitmap || new_value > 1)
|
||||
return;
|
||||
if (!new_value)
|
||||
bitmap[bit >> 3] &= ~(1 << (bit & 7));
|
||||
else
|
||||
bitmap[bit >> 3] |= (1 << (bit & 7));
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_bit_get - get value of a bit in a field of bits
|
||||
* @bitmap: field of bits
|
||||
* @bit: bit to get
|
||||
*
|
||||
* Get and return the value of the bit @bit in @bitmap (0 or 1).
|
||||
* Return -1 on error.
|
||||
*/
|
||||
char ntfs_bit_get(const u8 *bitmap, const u64 bit)
|
||||
{
|
||||
if (!bitmap)
|
||||
return -1;
|
||||
return (bitmap[bit >> 3] >> (bit & 7)) & 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_bit_get_and_set - get value of a bit in a field of bits and set it
|
||||
* @bitmap: field of bits
|
||||
* @bit: bit to get/set
|
||||
* @new_value: value to set bit to (0 or 1)
|
||||
*
|
||||
* Return the value of the bit @bit and set it to @new_value (0 or 1).
|
||||
* Return -1 on error.
|
||||
*/
|
||||
char ntfs_bit_get_and_set(u8 *bitmap, const u64 bit, const u8 new_value)
|
||||
{
|
||||
register u8 old_bit, shift;
|
||||
|
||||
if (!bitmap || new_value > 1)
|
||||
return -1;
|
||||
shift = bit & 7;
|
||||
old_bit = (bitmap[bit >> 3] >> shift) & 1;
|
||||
if (new_value != old_bit)
|
||||
bitmap[bit >> 3] ^= 1 << shift;
|
||||
return old_bit;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_bitmap_set_bits_in_run - set a run of bits in a bitmap to a value
|
||||
* @na: attribute containing the bitmap
|
||||
* @start_bit: first bit to set
|
||||
* @count: number of bits to set
|
||||
* @value: value to set the bits to (i.e. 0 or 1)
|
||||
*
|
||||
* Set @count bits starting at bit @start_bit in the bitmap described by the
|
||||
* attribute @na to @value, where @value is either 0 or 1.
|
||||
*
|
||||
* On success return 0 and on error return -1 with errno set to the error code.
|
||||
*/
|
||||
static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit,
|
||||
s64 count, int value)
|
||||
{
|
||||
s64 bufsize, br;
|
||||
u8 *buf, *lastbyte_buf;
|
||||
int bit, firstbyte, lastbyte, lastbyte_pos, tmp, ret = -1;
|
||||
|
||||
if (!na || start_bit < 0 || count < 0) {
|
||||
errno = EINVAL;
|
||||
ntfs_log_perror("%s: Invalid argument (%p, %lld, %lld)",
|
||||
__FUNCTION__, na, (long long)start_bit, (long long)count);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bit = start_bit & 7;
|
||||
if (bit)
|
||||
firstbyte = 1;
|
||||
else
|
||||
firstbyte = 0;
|
||||
|
||||
/* Calculate the required buffer size in bytes, capping it at 8kiB. */
|
||||
bufsize = ((count - (bit ? 8 - bit : 0) + 7) >> 3) + firstbyte;
|
||||
if (bufsize > 8192)
|
||||
bufsize = 8192;
|
||||
|
||||
buf = ntfs_malloc(bufsize);
|
||||
if (!buf)
|
||||
return -1;
|
||||
|
||||
/* Depending on @value, zero or set all bits in the allocated buffer. */
|
||||
memset(buf, value ? 0xff : 0, bufsize);
|
||||
|
||||
/* If there is a first partial byte... */
|
||||
if (bit) {
|
||||
/* read it in... */
|
||||
br = ntfs_attr_pread(na, start_bit >> 3, 1, buf);
|
||||
if (br != 1) {
|
||||
if (br >= 0)
|
||||
errno = EIO;
|
||||
goto free_err_out;
|
||||
}
|
||||
/* and set or clear the appropriate bits in it. */
|
||||
while ((bit & 7) && count--) {
|
||||
if (value)
|
||||
*buf |= 1 << bit++;
|
||||
else
|
||||
*buf &= ~(1 << bit++);
|
||||
}
|
||||
/* Update @start_bit to the new position. */
|
||||
start_bit = (start_bit + 7) & ~7;
|
||||
}
|
||||
|
||||
/* Loop until @count reaches zero. */
|
||||
lastbyte = 0;
|
||||
lastbyte_buf = NULL;
|
||||
bit = count & 7;
|
||||
do {
|
||||
/* If there is a last partial byte... */
|
||||
if (count > 0 && bit) {
|
||||
lastbyte_pos = ((count + 7) >> 3) + firstbyte;
|
||||
if (!lastbyte_pos) {
|
||||
// FIXME: Eeek! BUG!
|
||||
ntfs_log_error("Lastbyte is zero. Leaving "
|
||||
"inconsistent metadata.\n");
|
||||
errno = EIO;
|
||||
goto free_err_out;
|
||||
}
|
||||
/* and it is in the currently loaded bitmap window... */
|
||||
if (lastbyte_pos <= bufsize) {
|
||||
lastbyte_buf = buf + lastbyte_pos - 1;
|
||||
|
||||
/* read the byte in... */
|
||||
br = ntfs_attr_pread(na, (start_bit + count) >>
|
||||
3, 1, lastbyte_buf);
|
||||
if (br != 1) {
|
||||
// FIXME: Eeek! We need rollback! (AIA)
|
||||
if (br >= 0)
|
||||
errno = EIO;
|
||||
ntfs_log_perror("Reading of last byte "
|
||||
"failed (%lld). Leaving inconsistent "
|
||||
"metadata", (long long)br);
|
||||
goto free_err_out;
|
||||
}
|
||||
/* and set/clear the appropriate bits in it. */
|
||||
while (bit && count--) {
|
||||
if (value)
|
||||
*lastbyte_buf |= 1 << --bit;
|
||||
else
|
||||
*lastbyte_buf &= ~(1 << --bit);
|
||||
}
|
||||
/* We don't want to come back here... */
|
||||
bit = 0;
|
||||
/* We have a last byte that we have handled. */
|
||||
lastbyte = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Write the prepared buffer to disk. */
|
||||
tmp = (start_bit >> 3) - firstbyte;
|
||||
br = ntfs_attr_pwrite(na, tmp, bufsize, buf);
|
||||
if (br != bufsize) {
|
||||
// FIXME: Eeek! We need rollback! (AIA)
|
||||
if (br >= 0)
|
||||
errno = EIO;
|
||||
ntfs_log_perror("Failed to write buffer to bitmap "
|
||||
"(%lld != %lld). Leaving inconsistent metadata",
|
||||
(long long)br, (long long)bufsize);
|
||||
goto free_err_out;
|
||||
}
|
||||
|
||||
/* Update counters. */
|
||||
tmp = (bufsize - firstbyte - lastbyte) << 3;
|
||||
if (firstbyte) {
|
||||
firstbyte = 0;
|
||||
/*
|
||||
* Re-set the partial first byte so a subsequent write
|
||||
* of the buffer does not have stale, incorrect bits.
|
||||
*/
|
||||
*buf = value ? 0xff : 0;
|
||||
}
|
||||
start_bit += tmp;
|
||||
count -= tmp;
|
||||
if (bufsize > (tmp = (count + 7) >> 3))
|
||||
bufsize = tmp;
|
||||
|
||||
if (lastbyte && count != 0) {
|
||||
// FIXME: Eeek! BUG!
|
||||
ntfs_log_error("Last buffer but count is not zero "
|
||||
"(%lld). Leaving inconsistent metadata.\n",
|
||||
(long long)count);
|
||||
errno = EIO;
|
||||
goto free_err_out;
|
||||
}
|
||||
} while (count > 0);
|
||||
|
||||
ret = 0;
|
||||
|
||||
free_err_out:
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_bitmap_set_run - set a run of bits in a bitmap
|
||||
* @na: attribute containing the bitmap
|
||||
* @start_bit: first bit to set
|
||||
* @count: number of bits to set
|
||||
*
|
||||
* Set @count bits starting at bit @start_bit in the bitmap described by the
|
||||
* attribute @na.
|
||||
*
|
||||
* On success return 0 and on error return -1 with errno set to the error code.
|
||||
*/
|
||||
int ntfs_bitmap_set_run(ntfs_attr *na, s64 start_bit, s64 count)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ntfs_log_enter("Set from bit %lld, count %lld\n",
|
||||
(long long)start_bit, (long long)count);
|
||||
ret = ntfs_bitmap_set_bits_in_run(na, start_bit, count, 1);
|
||||
ntfs_log_leave("\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_bitmap_clear_run - clear a run of bits in a bitmap
|
||||
* @na: attribute containing the bitmap
|
||||
* @start_bit: first bit to clear
|
||||
* @count: number of bits to clear
|
||||
*
|
||||
* Clear @count bits starting at bit @start_bit in the bitmap described by the
|
||||
* attribute @na.
|
||||
*
|
||||
* On success return 0 and on error return -1 with errno set to the error code.
|
||||
*/
|
||||
int ntfs_bitmap_clear_run(ntfs_attr *na, s64 start_bit, s64 count)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ntfs_log_enter("Clear from bit %lld, count %lld\n",
|
||||
(long long)start_bit, (long long)count);
|
||||
ret = ntfs_bitmap_set_bits_in_run(na, start_bit, count, 0);
|
||||
ntfs_log_leave("\n");
|
||||
return ret;
|
||||
}
|
||||
|
96
source/bitmap.h
Normal file
96
source/bitmap.h
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* bitmap.h - Exports for bitmap handling. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2004 Anton Altaparmakov
|
||||
* Copyright (c) 2004-2005 Richard Russon
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_BITMAP_H
|
||||
#define _NTFS_BITMAP_H
|
||||
|
||||
#include "types.h"
|
||||
#include "attrib.h"
|
||||
|
||||
/*
|
||||
* NOTES:
|
||||
*
|
||||
* - Operations are 8-bit only to ensure the functions work both on little
|
||||
* and big endian machines! So don't make them 32-bit ops!
|
||||
* - bitmap starts at bit = 0 and ends at bit = bitmap size - 1.
|
||||
* - _Caller_ has to make sure that the bit to operate on is less than the
|
||||
* size of the bitmap.
|
||||
*/
|
||||
|
||||
extern void ntfs_bit_set(u8 *bitmap, const u64 bit, const u8 new_value);
|
||||
extern char ntfs_bit_get(const u8 *bitmap, const u64 bit);
|
||||
extern char ntfs_bit_get_and_set(u8 *bitmap, const u64 bit, const u8 new_value);
|
||||
extern int ntfs_bitmap_set_run(ntfs_attr *na, s64 start_bit, s64 count);
|
||||
extern int ntfs_bitmap_clear_run(ntfs_attr *na, s64 start_bit, s64 count);
|
||||
|
||||
/**
|
||||
* ntfs_bitmap_set_bit - set a bit in a bitmap
|
||||
* @na: attribute containing the bitmap
|
||||
* @bit: bit to set
|
||||
*
|
||||
* Set the @bit in the bitmap described by the attribute @na.
|
||||
*
|
||||
* On success return 0 and on error return -1 with errno set to the error code.
|
||||
*/
|
||||
static __inline__ int ntfs_bitmap_set_bit(ntfs_attr *na, s64 bit)
|
||||
{
|
||||
return ntfs_bitmap_set_run(na, bit, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_bitmap_clear_bit - clear a bit in a bitmap
|
||||
* @na: attribute containing the bitmap
|
||||
* @bit: bit to clear
|
||||
*
|
||||
* Clear @bit in the bitmap described by the attribute @na.
|
||||
*
|
||||
* On success return 0 and on error return -1 with errno set to the error code.
|
||||
*/
|
||||
static __inline__ int ntfs_bitmap_clear_bit(ntfs_attr *na, s64 bit)
|
||||
{
|
||||
return ntfs_bitmap_clear_run(na, bit, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* rol32 - rotate a 32-bit value left
|
||||
*
|
||||
* @word: value to rotate
|
||||
* @shift: bits to roll
|
||||
*/
|
||||
static __inline__ u32 ntfs_rol32(u32 word, unsigned int shift)
|
||||
{
|
||||
return (word << shift) | (word >> (32 - shift));
|
||||
}
|
||||
|
||||
/*
|
||||
* ror32 - rotate a 32-bit value right
|
||||
*
|
||||
* @word: value to rotate
|
||||
* @shift: bits to roll
|
||||
*/
|
||||
static __inline__ u32 ntfs_ror32(u32 word, unsigned int shift)
|
||||
{
|
||||
return (word >> shift) | (word << (32 - shift));
|
||||
}
|
||||
|
||||
#endif /* defined _NTFS_BITMAP_H */
|
||||
|
285
source/bootsect.c
Normal file
285
source/bootsect.c
Normal file
@ -0,0 +1,285 @@
|
||||
/**
|
||||
* bootsect.c - Boot sector handling code. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2006 Anton Altaparmakov
|
||||
* Copyright (c) 2003-2008 Szabolcs Szakacsits
|
||||
* Copyright (c) 2005 Yura Pakhuchiy
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "compat.h"
|
||||
#include "bootsect.h"
|
||||
#include "debug.h"
|
||||
#include "logging.h"
|
||||
|
||||
/**
|
||||
* ntfs_boot_sector_is_ntfs - check if buffer contains a valid ntfs boot sector
|
||||
* @b: buffer containing putative boot sector to analyze
|
||||
* @silent: if zero, output progress messages to stderr
|
||||
*
|
||||
* Check if the buffer @b contains a valid ntfs boot sector. The buffer @b
|
||||
* must be at least 512 bytes in size.
|
||||
*
|
||||
* If @silent is zero, output progress messages to stderr. Otherwise, do not
|
||||
* output any messages (except when configured with --enable-debug in which
|
||||
* case warning/debug messages may be displayed).
|
||||
*
|
||||
* Return TRUE if @b contains a valid ntfs boot sector and FALSE if not.
|
||||
*/
|
||||
BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b)
|
||||
{
|
||||
u32 i;
|
||||
BOOL ret = FALSE;
|
||||
|
||||
ntfs_log_debug("Beginning bootsector check.\n");
|
||||
|
||||
ntfs_log_debug("Checking OEMid, NTFS signature.\n");
|
||||
if (b->oem_id != cpu_to_le64(0x202020205346544eULL)) { /* "NTFS " */
|
||||
ntfs_log_error("NTFS signature is missing.\n");
|
||||
goto not_ntfs;
|
||||
}
|
||||
|
||||
ntfs_log_debug("Checking bytes per sector.\n");
|
||||
if (le16_to_cpu(b->bpb.bytes_per_sector) < 256 ||
|
||||
le16_to_cpu(b->bpb.bytes_per_sector) > 4096) {
|
||||
ntfs_log_error("Unexpected bytes per sector value (%d).\n",
|
||||
le16_to_cpu(b->bpb.bytes_per_sector));
|
||||
goto not_ntfs;
|
||||
}
|
||||
|
||||
ntfs_log_debug("Checking sectors per cluster.\n");
|
||||
switch (b->bpb.sectors_per_cluster) {
|
||||
case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128:
|
||||
break;
|
||||
default:
|
||||
ntfs_log_error("Unexpected sectors per cluster value (%d).\n",
|
||||
b->bpb.sectors_per_cluster);
|
||||
goto not_ntfs;
|
||||
}
|
||||
|
||||
ntfs_log_debug("Checking cluster size.\n");
|
||||
i = (u32)le16_to_cpu(b->bpb.bytes_per_sector) *
|
||||
b->bpb.sectors_per_cluster;
|
||||
if (i > 65536) {
|
||||
ntfs_log_error("Unexpected cluster size (%d).\n", i);
|
||||
goto not_ntfs;
|
||||
}
|
||||
|
||||
ntfs_log_debug("Checking reserved fields are zero.\n");
|
||||
if (le16_to_cpu(b->bpb.reserved_sectors) ||
|
||||
le16_to_cpu(b->bpb.root_entries) ||
|
||||
le16_to_cpu(b->bpb.sectors) ||
|
||||
le16_to_cpu(b->bpb.sectors_per_fat) ||
|
||||
le32_to_cpu(b->bpb.large_sectors) ||
|
||||
b->bpb.fats) {
|
||||
ntfs_log_error("Reserved fields aren't zero "
|
||||
"(%d, %d, %d, %d, %d, %d).\n",
|
||||
le16_to_cpu(b->bpb.reserved_sectors),
|
||||
le16_to_cpu(b->bpb.root_entries),
|
||||
le16_to_cpu(b->bpb.sectors),
|
||||
le16_to_cpu(b->bpb.sectors_per_fat),
|
||||
le32_to_cpu(b->bpb.large_sectors),
|
||||
b->bpb.fats);
|
||||
goto not_ntfs;
|
||||
}
|
||||
|
||||
ntfs_log_debug("Checking clusters per mft record.\n");
|
||||
if ((u8)b->clusters_per_mft_record < 0xe1 ||
|
||||
(u8)b->clusters_per_mft_record > 0xf7) {
|
||||
switch (b->clusters_per_mft_record) {
|
||||
case 1: case 2: case 4: case 8: case 0x10: case 0x20: case 0x40:
|
||||
break;
|
||||
default:
|
||||
ntfs_log_error("Unexpected clusters per mft record "
|
||||
"(%d).\n", b->clusters_per_mft_record);
|
||||
goto not_ntfs;
|
||||
}
|
||||
}
|
||||
|
||||
ntfs_log_debug("Checking clusters per index block.\n");
|
||||
if ((u8)b->clusters_per_index_record < 0xe1 ||
|
||||
(u8)b->clusters_per_index_record > 0xf7) {
|
||||
switch (b->clusters_per_index_record) {
|
||||
case 1: case 2: case 4: case 8: case 0x10: case 0x20: case 0x40:
|
||||
break;
|
||||
default:
|
||||
ntfs_log_error("Unexpected clusters per index record "
|
||||
"(%d).\n", b->clusters_per_index_record);
|
||||
goto not_ntfs;
|
||||
}
|
||||
}
|
||||
|
||||
if (b->end_of_sector_marker != cpu_to_le16(0xaa55))
|
||||
ntfs_log_debug("Warning: Bootsector has invalid end of sector "
|
||||
"marker.\n");
|
||||
|
||||
ntfs_log_debug("Bootsector check completed successfully.\n");
|
||||
|
||||
ret = TRUE;
|
||||
not_ntfs:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char *last_sector_error =
|
||||
"HINTS: Either the volume is a RAID/LDM but it wasn't setup yet,\n"
|
||||
" or it was not setup correctly (e.g. by not using mdadm --build ...),\n"
|
||||
" or a wrong device is tried to be mounted,\n"
|
||||
" or the partition table is corrupt (partition is smaller than NTFS),\n"
|
||||
" or the NTFS boot sector is corrupt (NTFS size is not valid).\n";
|
||||
|
||||
/**
|
||||
* ntfs_boot_sector_parse - setup an ntfs volume from an ntfs boot sector
|
||||
* @vol: ntfs_volume to setup
|
||||
* @bs: buffer containing ntfs boot sector to parse
|
||||
*
|
||||
* Parse the ntfs bootsector @bs and setup the ntfs volume @vol with the
|
||||
* obtained values.
|
||||
*
|
||||
* Return 0 on success or -1 on error with errno set to the error code EINVAL.
|
||||
*/
|
||||
int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs)
|
||||
{
|
||||
s64 sectors;
|
||||
u8 sectors_per_cluster;
|
||||
s8 c;
|
||||
|
||||
/* We return -1 with errno = EINVAL on error. */
|
||||
errno = EINVAL;
|
||||
|
||||
vol->sector_size = le16_to_cpu(bs->bpb.bytes_per_sector);
|
||||
vol->sector_size_bits = ffs(vol->sector_size) - 1;
|
||||
ntfs_log_debug("SectorSize = 0x%x\n", vol->sector_size);
|
||||
ntfs_log_debug("SectorSizeBits = %u\n", vol->sector_size_bits);
|
||||
/*
|
||||
* The bounds checks on mft_lcn and mft_mirr_lcn (i.e. them being
|
||||
* below or equal the number_of_clusters) really belong in the
|
||||
* ntfs_boot_sector_is_ntfs but in this way we can just do this once.
|
||||
*/
|
||||
sectors_per_cluster = bs->bpb.sectors_per_cluster;
|
||||
ntfs_log_debug("SectorsPerCluster = 0x%x\n", sectors_per_cluster);
|
||||
if (sectors_per_cluster & (sectors_per_cluster - 1)) {
|
||||
ntfs_log_error("sectors_per_cluster (%d) is not a power of 2."
|
||||
"\n", sectors_per_cluster);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sectors = sle64_to_cpu(bs->number_of_sectors);
|
||||
ntfs_log_debug("NumberOfSectors = %lld\n", (long long)sectors);
|
||||
if (!sectors) {
|
||||
ntfs_log_error("Volume size is set to zero.\n");
|
||||
return -1;
|
||||
}
|
||||
if (vol->dev->d_ops->seek(vol->dev,
|
||||
(sectors - 1) << vol->sector_size_bits,
|
||||
SEEK_SET) == -1) {
|
||||
ntfs_log_perror("Failed to read last sector (%lld)",
|
||||
(long long)sectors);
|
||||
ntfs_log_error("%s", last_sector_error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
vol->nr_clusters = sectors >> (ffs(sectors_per_cluster) - 1);
|
||||
|
||||
vol->mft_lcn = sle64_to_cpu(bs->mft_lcn);
|
||||
vol->mftmirr_lcn = sle64_to_cpu(bs->mftmirr_lcn);
|
||||
ntfs_log_debug("MFT LCN = %lld\n", (long long)vol->mft_lcn);
|
||||
ntfs_log_debug("MFTMirr LCN = %lld\n", (long long)vol->mftmirr_lcn);
|
||||
if (vol->mft_lcn > vol->nr_clusters ||
|
||||
vol->mftmirr_lcn > vol->nr_clusters) {
|
||||
ntfs_log_error("$MFT LCN (%lld) or $MFTMirr LCN (%lld) is "
|
||||
"greater than the number of clusters (%lld).\n",
|
||||
(long long)vol->mft_lcn, (long long)vol->mftmirr_lcn,
|
||||
(long long)vol->nr_clusters);
|
||||
return -1;
|
||||
}
|
||||
|
||||
vol->cluster_size = sectors_per_cluster * vol->sector_size;
|
||||
if (vol->cluster_size & (vol->cluster_size - 1)) {
|
||||
ntfs_log_error("cluster_size (%d) is not a power of 2.\n",
|
||||
vol->cluster_size);
|
||||
return -1;
|
||||
}
|
||||
vol->cluster_size_bits = ffs(vol->cluster_size) - 1;
|
||||
/*
|
||||
* Need to get the clusters per mft record and handle it if it is
|
||||
* negative. Then calculate the mft_record_size. A value of 0x80 is
|
||||
* illegal, thus signed char is actually ok!
|
||||
*/
|
||||
c = bs->clusters_per_mft_record;
|
||||
ntfs_log_debug("ClusterSize = 0x%x\n", (unsigned)vol->cluster_size);
|
||||
ntfs_log_debug("ClusterSizeBits = %u\n", vol->cluster_size_bits);
|
||||
ntfs_log_debug("ClustersPerMftRecord = 0x%x\n", c);
|
||||
/*
|
||||
* When clusters_per_mft_record is negative, it means that it is to
|
||||
* be taken to be the negative base 2 logarithm of the mft_record_size
|
||||
* min bytes. Then:
|
||||
* mft_record_size = 2^(-clusters_per_mft_record) bytes.
|
||||
*/
|
||||
if (c < 0)
|
||||
vol->mft_record_size = 1 << -c;
|
||||
else
|
||||
vol->mft_record_size = c << vol->cluster_size_bits;
|
||||
if (vol->mft_record_size & (vol->mft_record_size - 1)) {
|
||||
ntfs_log_error("mft_record_size (%d) is not a power of 2.\n",
|
||||
vol->mft_record_size);
|
||||
return -1;
|
||||
}
|
||||
vol->mft_record_size_bits = ffs(vol->mft_record_size) - 1;
|
||||
ntfs_log_debug("MftRecordSize = 0x%x\n", (unsigned)vol->mft_record_size);
|
||||
ntfs_log_debug("MftRecordSizeBits = %u\n", vol->mft_record_size_bits);
|
||||
/* Same as above for INDX record. */
|
||||
c = bs->clusters_per_index_record;
|
||||
ntfs_log_debug("ClustersPerINDXRecord = 0x%x\n", c);
|
||||
if (c < 0)
|
||||
vol->indx_record_size = 1 << -c;
|
||||
else
|
||||
vol->indx_record_size = c << vol->cluster_size_bits;
|
||||
vol->indx_record_size_bits = ffs(vol->indx_record_size) - 1;
|
||||
ntfs_log_debug("INDXRecordSize = 0x%x\n", (unsigned)vol->indx_record_size);
|
||||
ntfs_log_debug("INDXRecordSizeBits = %u\n", vol->indx_record_size_bits);
|
||||
/*
|
||||
* Work out the size of the MFT mirror in number of mft records. If the
|
||||
* cluster size is less than or equal to the size taken by four mft
|
||||
* records, the mft mirror stores the first four mft records. If the
|
||||
* cluster size is bigger than the size taken by four mft records, the
|
||||
* mft mirror contains as many mft records as will fit into one
|
||||
* cluster.
|
||||
*/
|
||||
if (vol->cluster_size <= 4 * vol->mft_record_size)
|
||||
vol->mftmirr_size = 4;
|
||||
else
|
||||
vol->mftmirr_size = vol->cluster_size / vol->mft_record_size;
|
||||
return 0;
|
||||
}
|
||||
|
42
source/bootsect.h
Normal file
42
source/bootsect.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* bootsect.h - Exports for bootsector record handling.
|
||||
* Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2002 Anton Altaparmakov
|
||||
* Copyright (c) 2006 Szabolcs Szakacsits
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_BOOTSECT_H
|
||||
#define _NTFS_BOOTSECT_H
|
||||
|
||||
#include "types.h"
|
||||
#include "volume.h"
|
||||
#include "layout.h"
|
||||
|
||||
/**
|
||||
* ntfs_boot_sector_is_ntfs - check a boot sector for describing an ntfs volume
|
||||
* @b: buffer containing the boot sector
|
||||
*
|
||||
* This function checks the boot sector in @b for describing a valid ntfs
|
||||
* volume. Return TRUE if @b is a valid NTFS boot sector or FALSE otherwise.
|
||||
*/
|
||||
extern BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b);
|
||||
extern int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs);
|
||||
|
||||
#endif /* defined _NTFS_BOOTSECT_H */
|
||||
|
609
source/cache.c
Normal file
609
source/cache.c
Normal file
@ -0,0 +1,609 @@
|
||||
/**
|
||||
* cache.c : deal with LRU caches
|
||||
*
|
||||
* Copyright (c) 2008-2009 Jean-Pierre Andre
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "security.h"
|
||||
#include "cache.h"
|
||||
#include "misc.h"
|
||||
#include "logging.h"
|
||||
|
||||
/*
|
||||
* General functions to deal with LRU caches
|
||||
*
|
||||
* The cached data have to be organized in a structure in which
|
||||
* the first fields must follow a mandatory pattern and further
|
||||
* fields may contain any fixed size data. They are stored in an
|
||||
* LRU list.
|
||||
*
|
||||
* A compare function must be provided for finding a wanted entry
|
||||
* in the cache. Another function may be provided for invalidating
|
||||
* an entry to facilitate multiple invalidation.
|
||||
*
|
||||
* These functions never return error codes. When there is a
|
||||
* shortage of memory, data is simply not cached.
|
||||
* When there is a hashing bug, hashing is dropped, and sequential
|
||||
* searches are used.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Enter a new hash index, after a new record has been inserted
|
||||
*
|
||||
* Do not call when a record has been modified (with no key change)
|
||||
*/
|
||||
|
||||
static void inserthashindex(struct CACHE_HEADER *cache,
|
||||
struct CACHED_GENERIC *current)
|
||||
{
|
||||
int h;
|
||||
struct HASH_ENTRY *link;
|
||||
struct HASH_ENTRY *first;
|
||||
|
||||
if (cache->dohash) {
|
||||
h = cache->dohash(current);
|
||||
if ((h >= 0) && (h < cache->max_hash)) {
|
||||
/* get a free link and insert at top of hash list */
|
||||
link = cache->free_hash;
|
||||
if (link) {
|
||||
cache->free_hash = link->next;
|
||||
first = cache->first_hash[h];
|
||||
if (first)
|
||||
link->next = first;
|
||||
else
|
||||
link->next = NULL;
|
||||
link->entry = current;
|
||||
cache->first_hash[h] = link;
|
||||
} else {
|
||||
ntfs_log_error("No more hash entries,"
|
||||
" cache %s hashing dropped\n",
|
||||
cache->name);
|
||||
cache->dohash = (cache_hash)NULL;
|
||||
}
|
||||
} else {
|
||||
ntfs_log_error("Illegal hash value,"
|
||||
" cache %s hashing dropped\n",
|
||||
cache->name);
|
||||
cache->dohash = (cache_hash)NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Drop a hash index when a record is about to be deleted
|
||||
*/
|
||||
|
||||
static void drophashindex(struct CACHE_HEADER *cache,
|
||||
const struct CACHED_GENERIC *current, int hash)
|
||||
{
|
||||
struct HASH_ENTRY *link;
|
||||
struct HASH_ENTRY *previous;
|
||||
|
||||
if (cache->dohash) {
|
||||
if ((hash >= 0) && (hash < cache->max_hash)) {
|
||||
/* find the link and unlink */
|
||||
link = cache->first_hash[hash];
|
||||
previous = (struct HASH_ENTRY*)NULL;
|
||||
while (link && (link->entry != current)) {
|
||||
previous = link;
|
||||
link = link->next;
|
||||
}
|
||||
if (link) {
|
||||
if (previous)
|
||||
previous->next = link->next;
|
||||
else
|
||||
cache->first_hash[hash] = link->next;
|
||||
link->next = cache->free_hash;
|
||||
cache->free_hash = link;
|
||||
} else {
|
||||
ntfs_log_error("Bad hash list,"
|
||||
" cache %s hashing dropped\n",
|
||||
cache->name);
|
||||
cache->dohash = (cache_hash)NULL;
|
||||
}
|
||||
} else {
|
||||
ntfs_log_error("Illegal hash value,"
|
||||
" cache %s hashing dropped\n",
|
||||
cache->name);
|
||||
cache->dohash = (cache_hash)NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetch an entry from cache
|
||||
*
|
||||
* returns the cache entry, or NULL if not available
|
||||
* The returned entry may be modified, but not freed
|
||||
*/
|
||||
|
||||
struct CACHED_GENERIC *ntfs_fetch_cache(struct CACHE_HEADER *cache,
|
||||
const struct CACHED_GENERIC *wanted, cache_compare compare)
|
||||
{
|
||||
struct CACHED_GENERIC *current;
|
||||
struct CACHED_GENERIC *previous;
|
||||
struct HASH_ENTRY *link;
|
||||
int h;
|
||||
|
||||
current = (struct CACHED_GENERIC*)NULL;
|
||||
if (cache) {
|
||||
if (cache->dohash) {
|
||||
/*
|
||||
* When possible, use the hash table to
|
||||
* locate the entry if present
|
||||
*/
|
||||
h = cache->dohash(wanted);
|
||||
link = cache->first_hash[h];
|
||||
while (link && compare(link->entry, wanted))
|
||||
link = link->next;
|
||||
if (link)
|
||||
current = link->entry;
|
||||
}
|
||||
if (!cache->dohash) {
|
||||
/*
|
||||
* Search sequentially in LRU list if no hash table
|
||||
* or if hashing has just failed
|
||||
*/
|
||||
current = cache->most_recent_entry;
|
||||
while (current
|
||||
&& compare(current, wanted)) {
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
if (current) {
|
||||
previous = current->previous;
|
||||
cache->hits++;
|
||||
if (previous) {
|
||||
/*
|
||||
* found and not at head of list, unlink from current
|
||||
* position and relink as head of list
|
||||
*/
|
||||
previous->next = current->next;
|
||||
if (current->next)
|
||||
current->next->previous
|
||||
= current->previous;
|
||||
else
|
||||
cache->oldest_entry
|
||||
= current->previous;
|
||||
current->next = cache->most_recent_entry;
|
||||
current->previous
|
||||
= (struct CACHED_GENERIC*)NULL;
|
||||
cache->most_recent_entry->previous = current;
|
||||
cache->most_recent_entry = current;
|
||||
}
|
||||
}
|
||||
cache->reads++;
|
||||
}
|
||||
return (current);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enter an inode number into cache
|
||||
* returns the cache entry or NULL if not possible
|
||||
*/
|
||||
|
||||
struct CACHED_GENERIC *ntfs_enter_cache(struct CACHE_HEADER *cache,
|
||||
const struct CACHED_GENERIC *item,
|
||||
cache_compare compare)
|
||||
{
|
||||
struct CACHED_GENERIC *current;
|
||||
struct CACHED_GENERIC *before;
|
||||
struct HASH_ENTRY *link;
|
||||
int h;
|
||||
|
||||
current = (struct CACHED_GENERIC*)NULL;
|
||||
if (cache) {
|
||||
if (cache->dohash) {
|
||||
/*
|
||||
* When possible, use the hash table to
|
||||
* find out whether the entry if present
|
||||
*/
|
||||
h = cache->dohash(item);
|
||||
link = cache->first_hash[h];
|
||||
while (link && compare(link->entry, item))
|
||||
link = link->next;
|
||||
if (link) {
|
||||
current = link->entry;
|
||||
}
|
||||
}
|
||||
if (!cache->dohash) {
|
||||
/*
|
||||
* Search sequentially in LRU list to locate the end,
|
||||
* and find out whether the entry is already in list
|
||||
* As we normally go to the end, no statistics is
|
||||
* kept.
|
||||
*/
|
||||
current = cache->most_recent_entry;
|
||||
while (current
|
||||
&& compare(current, item)) {
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
|
||||
if (!current) {
|
||||
/*
|
||||
* Not in list, get a free entry or reuse the
|
||||
* last entry, and relink as head of list
|
||||
* Note : we assume at least three entries, so
|
||||
* before, previous and first are different when
|
||||
* an entry is reused.
|
||||
*/
|
||||
|
||||
if (cache->free_entry) {
|
||||
current = cache->free_entry;
|
||||
cache->free_entry = cache->free_entry->next;
|
||||
if (item->varsize) {
|
||||
current->variable = ntfs_malloc(
|
||||
item->varsize);
|
||||
} else
|
||||
current->variable = (void*)NULL;
|
||||
current->varsize = item->varsize;
|
||||
if (!cache->oldest_entry)
|
||||
cache->oldest_entry = current;
|
||||
} else {
|
||||
/* reusing the oldest entry */
|
||||
current = cache->oldest_entry;
|
||||
before = current->previous;
|
||||
before->next = (struct CACHED_GENERIC*)NULL;
|
||||
if (cache->dohash)
|
||||
drophashindex(cache,current,
|
||||
cache->dohash(current));
|
||||
if (cache->dofree)
|
||||
cache->dofree(current);
|
||||
cache->oldest_entry = current->previous;
|
||||
if (item->varsize) {
|
||||
if (current->varsize)
|
||||
current->variable = realloc(
|
||||
current->variable,
|
||||
item->varsize);
|
||||
else
|
||||
current->variable = ntfs_malloc(
|
||||
item->varsize);
|
||||
} else {
|
||||
if (current->varsize)
|
||||
free(current->variable);
|
||||
current->variable = (void*)NULL;
|
||||
}
|
||||
current->varsize = item->varsize;
|
||||
}
|
||||
current->next = cache->most_recent_entry;
|
||||
current->previous = (struct CACHED_GENERIC*)NULL;
|
||||
if (cache->most_recent_entry)
|
||||
cache->most_recent_entry->previous = current;
|
||||
cache->most_recent_entry = current;
|
||||
memcpy(current->fixed, item->fixed, cache->fixed_size);
|
||||
if (item->varsize) {
|
||||
if (current->variable) {
|
||||
memcpy(current->variable,
|
||||
item->variable, item->varsize);
|
||||
} else {
|
||||
/*
|
||||
* no more memory for variable part
|
||||
* recycle entry in free list
|
||||
* not an error, just uncacheable
|
||||
*/
|
||||
cache->most_recent_entry = current->next;
|
||||
current->next = cache->free_entry;
|
||||
cache->free_entry = current;
|
||||
current = (struct CACHED_GENERIC*)NULL;
|
||||
}
|
||||
} else {
|
||||
current->variable = (void*)NULL;
|
||||
current->varsize = 0;
|
||||
}
|
||||
if (cache->dohash && current)
|
||||
inserthashindex(cache,current);
|
||||
}
|
||||
cache->writes++;
|
||||
}
|
||||
return (current);
|
||||
}
|
||||
|
||||
/*
|
||||
* Invalidate a cache entry
|
||||
* The entry is moved to the free entry list
|
||||
* A specific function may be called for entry deletion
|
||||
*/
|
||||
|
||||
static void do_invalidate(struct CACHE_HEADER *cache,
|
||||
struct CACHED_GENERIC *current, int flags)
|
||||
{
|
||||
struct CACHED_GENERIC *previous;
|
||||
|
||||
previous = current->previous;
|
||||
if ((flags & CACHE_FREE) && cache->dofree)
|
||||
cache->dofree(current);
|
||||
/*
|
||||
* Relink into free list
|
||||
*/
|
||||
if (current->next)
|
||||
current->next->previous = current->previous;
|
||||
else
|
||||
cache->oldest_entry = current->previous;
|
||||
if (previous)
|
||||
previous->next = current->next;
|
||||
else
|
||||
cache->most_recent_entry = current->next;
|
||||
current->next = cache->free_entry;
|
||||
cache->free_entry = current;
|
||||
if (current->variable)
|
||||
free(current->variable);
|
||||
current->varsize = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Invalidate entries in cache
|
||||
*
|
||||
* Several entries may have to be invalidated (at least for inodes
|
||||
* associated to directories which have been renamed), a different
|
||||
* compare function may be provided to select entries to invalidate
|
||||
*
|
||||
* Returns the number of deleted entries, this can be used by
|
||||
* the caller to signal a cache corruption if the entry was
|
||||
* supposed to be found.
|
||||
*/
|
||||
|
||||
int ntfs_invalidate_cache(struct CACHE_HEADER *cache,
|
||||
const struct CACHED_GENERIC *item, cache_compare compare,
|
||||
int flags)
|
||||
{
|
||||
struct CACHED_GENERIC *current;
|
||||
struct CACHED_GENERIC *previous;
|
||||
struct CACHED_GENERIC *next;
|
||||
struct HASH_ENTRY *link;
|
||||
int count;
|
||||
int h;
|
||||
|
||||
current = (struct CACHED_GENERIC*)NULL;
|
||||
count = 0;
|
||||
if (cache) {
|
||||
if (!(flags & CACHE_NOHASH) && cache->dohash) {
|
||||
/*
|
||||
* When possible, use the hash table to
|
||||
* find out whether the entry if present
|
||||
*/
|
||||
h = cache->dohash(item);
|
||||
link = cache->first_hash[h];
|
||||
while (link) {
|
||||
if (compare(link->entry, item))
|
||||
link = link->next;
|
||||
else {
|
||||
current = link->entry;
|
||||
link = link->next;
|
||||
if (current) {
|
||||
drophashindex(cache,current,h);
|
||||
do_invalidate(cache,
|
||||
current,flags);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((flags & CACHE_NOHASH) || !cache->dohash) {
|
||||
/*
|
||||
* Search sequentially in LRU list
|
||||
*/
|
||||
current = cache->most_recent_entry;
|
||||
previous = (struct CACHED_GENERIC*)NULL;
|
||||
while (current) {
|
||||
if (!compare(current, item)) {
|
||||
next = current->next;
|
||||
if (cache->dohash)
|
||||
drophashindex(cache,current,
|
||||
cache->dohash(current));
|
||||
do_invalidate(cache,current,flags);
|
||||
current = next;
|
||||
count++;
|
||||
} else {
|
||||
previous = current;
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return (count);
|
||||
}
|
||||
|
||||
int ntfs_remove_cache(struct CACHE_HEADER *cache,
|
||||
struct CACHED_GENERIC *item, int flags)
|
||||
{
|
||||
int count;
|
||||
|
||||
count = 0;
|
||||
if (cache) {
|
||||
if (cache->dohash)
|
||||
drophashindex(cache,item,cache->dohash(item));
|
||||
do_invalidate(cache,item,flags);
|
||||
count++;
|
||||
}
|
||||
return (count);
|
||||
}
|
||||
|
||||
/*
|
||||
* Free memory allocated to a cache
|
||||
*/
|
||||
|
||||
static void ntfs_free_cache(struct CACHE_HEADER *cache)
|
||||
{
|
||||
struct CACHED_GENERIC *entry;
|
||||
|
||||
if (cache) {
|
||||
for (entry=cache->most_recent_entry; entry; entry=entry->next) {
|
||||
if (cache->dofree)
|
||||
cache->dofree(entry);
|
||||
if (entry->variable)
|
||||
free(entry->variable);
|
||||
}
|
||||
free(cache);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a cache
|
||||
*
|
||||
* Returns the cache header, or NULL if the cache could not be created
|
||||
*/
|
||||
|
||||
static struct CACHE_HEADER *ntfs_create_cache(const char *name,
|
||||
cache_free dofree, cache_hash dohash,
|
||||
int full_item_size,
|
||||
int item_count, int max_hash)
|
||||
{
|
||||
struct CACHE_HEADER *cache;
|
||||
struct CACHED_GENERIC *pc;
|
||||
struct CACHED_GENERIC *qc;
|
||||
struct HASH_ENTRY *ph;
|
||||
struct HASH_ENTRY *qh;
|
||||
struct HASH_ENTRY **px;
|
||||
size_t size;
|
||||
int i;
|
||||
|
||||
size = sizeof(struct CACHE_HEADER) + item_count*full_item_size;
|
||||
if (max_hash)
|
||||
size += item_count*sizeof(struct HASH_ENTRY)
|
||||
+ max_hash*sizeof(struct HASH_ENTRY*);
|
||||
cache = (struct CACHE_HEADER*)ntfs_malloc(size);
|
||||
if (cache) {
|
||||
/* header */
|
||||
cache->name = name;
|
||||
cache->dofree = dofree;
|
||||
if (dohash && max_hash) {
|
||||
cache->dohash = dohash;
|
||||
cache->max_hash = max_hash;
|
||||
} else {
|
||||
cache->dohash = (cache_hash)NULL;
|
||||
cache->max_hash = 0;
|
||||
}
|
||||
cache->fixed_size = full_item_size - sizeof(struct CACHED_GENERIC);
|
||||
cache->reads = 0;
|
||||
cache->writes = 0;
|
||||
cache->hits = 0;
|
||||
/* chain the data entries, and mark an invalid entry */
|
||||
cache->most_recent_entry = (struct CACHED_GENERIC*)NULL;
|
||||
cache->oldest_entry = (struct CACHED_GENERIC*)NULL;
|
||||
cache->free_entry = &cache->entry[0];
|
||||
pc = &cache->entry[0];
|
||||
for (i=0; i<(item_count - 1); i++) {
|
||||
qc = (struct CACHED_GENERIC*)((char*)pc
|
||||
+ full_item_size);
|
||||
pc->next = qc;
|
||||
pc->variable = (void*)NULL;
|
||||
pc->varsize = 0;
|
||||
pc = qc;
|
||||
}
|
||||
/* special for the last entry */
|
||||
pc->next = (struct CACHED_GENERIC*)NULL;
|
||||
pc->variable = (void*)NULL;
|
||||
pc->varsize = 0;
|
||||
|
||||
if (max_hash) {
|
||||
/* chain the hash entries */
|
||||
ph = (struct HASH_ENTRY*)(((char*)pc) + full_item_size);
|
||||
cache->free_hash = ph;
|
||||
for (i=0; i<(item_count - 1); i++) {
|
||||
qh = &ph[1];
|
||||
ph->next = qh;
|
||||
ph = qh;
|
||||
}
|
||||
/* special for the last entry */
|
||||
if (item_count) {
|
||||
ph->next = (struct HASH_ENTRY*)NULL;
|
||||
}
|
||||
/* create and initialize the hash indexes */
|
||||
px = (struct HASH_ENTRY**)&ph[1];
|
||||
cache->first_hash = px;
|
||||
for (i=0; i<max_hash; i++)
|
||||
px[i] = (struct HASH_ENTRY*)NULL;
|
||||
} else {
|
||||
cache->free_hash = (struct HASH_ENTRY*)NULL;
|
||||
cache->first_hash = (struct HASH_ENTRY**)NULL;
|
||||
}
|
||||
}
|
||||
return (cache);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create all LRU caches
|
||||
*
|
||||
* No error return, if creation is not possible, cacheing will
|
||||
* just be not available
|
||||
*/
|
||||
|
||||
void ntfs_create_lru_caches(ntfs_volume *vol)
|
||||
{
|
||||
#if CACHE_INODE_SIZE
|
||||
/* inode cache */
|
||||
vol->xinode_cache = ntfs_create_cache("inode",(cache_free)NULL,
|
||||
ntfs_dir_inode_hash, sizeof(struct CACHED_INODE),
|
||||
CACHE_INODE_SIZE, 2*CACHE_INODE_SIZE);
|
||||
#endif
|
||||
#if CACHE_NIDATA_SIZE
|
||||
/* idata cache */
|
||||
vol->nidata_cache = ntfs_create_cache("nidata",
|
||||
ntfs_inode_nidata_free, ntfs_inode_nidata_hash,
|
||||
sizeof(struct CACHED_NIDATA),
|
||||
CACHE_NIDATA_SIZE, 2*CACHE_NIDATA_SIZE);
|
||||
#endif
|
||||
#if CACHE_LOOKUP_SIZE
|
||||
/* lookup cache */
|
||||
vol->lookup_cache = ntfs_create_cache("lookup",
|
||||
(cache_free)NULL, ntfs_dir_lookup_hash,
|
||||
sizeof(struct CACHED_LOOKUP),
|
||||
CACHE_LOOKUP_SIZE, 2*CACHE_LOOKUP_SIZE);
|
||||
#endif
|
||||
vol->securid_cache = ntfs_create_cache("securid",(cache_free)NULL,
|
||||
(cache_hash)NULL,sizeof(struct CACHED_SECURID), CACHE_SECURID_SIZE, 0);
|
||||
#if CACHE_LEGACY_SIZE
|
||||
vol->legacy_cache = ntfs_create_cache("legacy",(cache_free)NULL,
|
||||
(cache_hash)NULL, sizeof(struct CACHED_PERMISSIONS_LEGACY), CACHE_LEGACY_SIZE, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Free all LRU caches
|
||||
*/
|
||||
|
||||
void ntfs_free_lru_caches(ntfs_volume *vol)
|
||||
{
|
||||
#if CACHE_INODE_SIZE
|
||||
ntfs_free_cache(vol->xinode_cache);
|
||||
#endif
|
||||
#if CACHE_NIDATA_SIZE
|
||||
ntfs_free_cache(vol->nidata_cache);
|
||||
#endif
|
||||
#if CACHE_LOOKUP_SIZE
|
||||
ntfs_free_cache(vol->lookup_cache);
|
||||
#endif
|
||||
ntfs_free_cache(vol->securid_cache);
|
||||
#if CACHE_LEGACY_SIZE
|
||||
ntfs_free_cache(vol->legacy_cache);
|
||||
#endif
|
||||
}
|
119
source/cache.h
Normal file
119
source/cache.h
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* cache.h : deal with indexed LRU caches
|
||||
*
|
||||
* Copyright (c) 2008-2009 Jean-Pierre Andre
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_CACHE_H_
|
||||
#define _NTFS_CACHE_H_
|
||||
|
||||
#include "volume.h"
|
||||
|
||||
struct CACHED_GENERIC {
|
||||
struct CACHED_GENERIC *next;
|
||||
struct CACHED_GENERIC *previous;
|
||||
void *variable;
|
||||
size_t varsize;
|
||||
union {
|
||||
/* force alignment for pointers and u64 */
|
||||
u64 u64align;
|
||||
void *ptralign;
|
||||
} fixed[0];
|
||||
} ;
|
||||
|
||||
struct CACHED_INODE {
|
||||
struct CACHED_INODE *next;
|
||||
struct CACHED_INODE *previous;
|
||||
const char *pathname;
|
||||
size_t varsize;
|
||||
/* above fields must match "struct CACHED_GENERIC" */
|
||||
u64 inum;
|
||||
} ;
|
||||
|
||||
struct CACHED_NIDATA {
|
||||
struct CACHED_NIDATA *next;
|
||||
struct CACHED_NIDATA *previous;
|
||||
const char *pathname; /* not used */
|
||||
size_t varsize; /* not used */
|
||||
/* above fields must match "struct CACHED_GENERIC" */
|
||||
u64 inum;
|
||||
ntfs_inode *ni;
|
||||
} ;
|
||||
|
||||
struct CACHED_LOOKUP {
|
||||
struct CACHED_LOOKUP *next;
|
||||
struct CACHED_LOOKUP *previous;
|
||||
const char *name;
|
||||
size_t namesize;
|
||||
/* above fields must match "struct CACHED_GENERIC" */
|
||||
u64 parent;
|
||||
u64 inum;
|
||||
} ;
|
||||
|
||||
enum {
|
||||
CACHE_FREE = 1,
|
||||
CACHE_NOHASH = 2
|
||||
} ;
|
||||
|
||||
typedef int (*cache_compare)(const struct CACHED_GENERIC *cached,
|
||||
const struct CACHED_GENERIC *item);
|
||||
typedef void (*cache_free)(const struct CACHED_GENERIC *cached);
|
||||
typedef int (*cache_hash)(const struct CACHED_GENERIC *cached);
|
||||
|
||||
struct HASH_ENTRY {
|
||||
struct HASH_ENTRY *next;
|
||||
struct CACHED_GENERIC *entry;
|
||||
} ;
|
||||
|
||||
struct CACHE_HEADER {
|
||||
const char *name;
|
||||
struct CACHED_GENERIC *most_recent_entry;
|
||||
struct CACHED_GENERIC *oldest_entry;
|
||||
struct CACHED_GENERIC *free_entry;
|
||||
struct HASH_ENTRY *free_hash;
|
||||
struct HASH_ENTRY **first_hash;
|
||||
cache_free dofree;
|
||||
cache_hash dohash;
|
||||
unsigned long reads;
|
||||
unsigned long writes;
|
||||
unsigned long hits;
|
||||
int fixed_size;
|
||||
int max_hash;
|
||||
struct CACHED_GENERIC entry[0];
|
||||
} ;
|
||||
|
||||
/* cast to generic, avoiding gcc warnings */
|
||||
#define GENERIC(pstr) ((const struct CACHED_GENERIC*)(const void*)(pstr))
|
||||
|
||||
struct CACHED_GENERIC *ntfs_fetch_cache(struct CACHE_HEADER *cache,
|
||||
const struct CACHED_GENERIC *wanted,
|
||||
cache_compare compare);
|
||||
struct CACHED_GENERIC *ntfs_enter_cache(struct CACHE_HEADER *cache,
|
||||
const struct CACHED_GENERIC *item,
|
||||
cache_compare compare);
|
||||
int ntfs_invalidate_cache(struct CACHE_HEADER *cache,
|
||||
const struct CACHED_GENERIC *item,
|
||||
cache_compare compare, int flags);
|
||||
int ntfs_remove_cache(struct CACHE_HEADER *cache,
|
||||
struct CACHED_GENERIC *item, int flags);
|
||||
|
||||
void ntfs_create_lru_caches(ntfs_volume *vol);
|
||||
void ntfs_free_lru_caches(ntfs_volume *vol);
|
||||
|
||||
#endif /* _NTFS_CACHE_H_ */
|
||||
|
374
source/cache2.c
Normal file
374
source/cache2.c
Normal file
@ -0,0 +1,374 @@
|
||||
/*
|
||||
cache.c
|
||||
The cache is not visible to the user. It should be flushed
|
||||
when any file is closed or changes are made to the filesystem.
|
||||
|
||||
This cache implements a least-used-page replacement policy. This will
|
||||
distribute sectors evenly over the pages, so if less than the maximum
|
||||
pages are used at once, they should all eventually remain in the cache.
|
||||
This also has the benefit of throwing out old sectors, so as not to keep
|
||||
too many stale pages around.
|
||||
|
||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
Copyright (c) 2009 shareese, rodries
|
||||
Copyright (c) 2010 Dimok
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <ogc/lwp_watchdog.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "cache2.h"
|
||||
#include "bit_ops.h"
|
||||
#include "mem_allocate.h"
|
||||
|
||||
#define CACHE_FREE UINT_MAX
|
||||
|
||||
NTFS_CACHE* _NTFS_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition, sec_t sectorSize) {
|
||||
NTFS_CACHE* cache;
|
||||
unsigned int i;
|
||||
NTFS_CACHE_ENTRY* cacheEntries;
|
||||
|
||||
if(numberOfPages==0 || sectorsPerPage==0) return NULL;
|
||||
|
||||
if (numberOfPages < 4) {
|
||||
numberOfPages = 4;
|
||||
}
|
||||
|
||||
if (sectorsPerPage < 32) {
|
||||
sectorsPerPage = 32;
|
||||
}
|
||||
|
||||
cache = (NTFS_CACHE*) ntfs_alloc (sizeof(NTFS_CACHE));
|
||||
if (cache == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cache->disc = discInterface;
|
||||
cache->endOfPartition = endOfPartition;
|
||||
cache->numberOfPages = numberOfPages;
|
||||
cache->sectorsPerPage = sectorsPerPage;
|
||||
cache->sectorSize = sectorSize;
|
||||
|
||||
|
||||
cacheEntries = (NTFS_CACHE_ENTRY*) ntfs_alloc ( sizeof(NTFS_CACHE_ENTRY) * numberOfPages);
|
||||
if (cacheEntries == NULL) {
|
||||
ntfs_free (cache);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < numberOfPages; i++) {
|
||||
cacheEntries[i].sector = CACHE_FREE;
|
||||
cacheEntries[i].count = 0;
|
||||
cacheEntries[i].last_access = 0;
|
||||
cacheEntries[i].dirty = false;
|
||||
cacheEntries[i].cache = (uint8_t*) ntfs_align ( sectorsPerPage * cache->sectorSize );
|
||||
}
|
||||
|
||||
cache->cacheEntries = cacheEntries;
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
void _NTFS_cache_destructor (NTFS_CACHE* cache) {
|
||||
unsigned int i;
|
||||
|
||||
if(cache==NULL) return;
|
||||
|
||||
// Clear out cache before destroying it
|
||||
_NTFS_cache_flush(cache);
|
||||
|
||||
// Free memory in reverse allocation order
|
||||
for (i = 0; i < cache->numberOfPages; i++) {
|
||||
ntfs_free (cache->cacheEntries[i].cache);
|
||||
}
|
||||
ntfs_free (cache->cacheEntries);
|
||||
ntfs_free (cache);
|
||||
}
|
||||
|
||||
static u32 accessCounter = 0;
|
||||
|
||||
static u32 accessTime(){
|
||||
accessCounter++;
|
||||
return accessCounter;
|
||||
}
|
||||
|
||||
static NTFS_CACHE_ENTRY* _NTFS_cache_getPage(NTFS_CACHE *cache,sec_t sector)
|
||||
{
|
||||
unsigned int i;
|
||||
NTFS_CACHE_ENTRY* cacheEntries = cache->cacheEntries;
|
||||
unsigned int numberOfPages = cache->numberOfPages;
|
||||
unsigned int sectorsPerPage = cache->sectorsPerPage;
|
||||
|
||||
bool foundFree = false;
|
||||
unsigned int oldUsed = 0;
|
||||
unsigned int oldAccess = UINT_MAX;
|
||||
|
||||
for(i=0;i<numberOfPages;i++) {
|
||||
if(sector>=cacheEntries[i].sector && sector<(cacheEntries[i].sector + cacheEntries[i].count)) {
|
||||
cacheEntries[i].last_access = accessTime();
|
||||
return &(cacheEntries[i]);
|
||||
}
|
||||
|
||||
if(foundFree==false && (cacheEntries[i].sector==CACHE_FREE || cacheEntries[i].last_access<oldAccess)) {
|
||||
if(cacheEntries[i].sector==CACHE_FREE) foundFree = true;
|
||||
oldUsed = i;
|
||||
oldAccess = cacheEntries[i].last_access;
|
||||
}
|
||||
}
|
||||
|
||||
if(foundFree==false && cacheEntries[oldUsed].dirty==true) {
|
||||
if(!cache->disc->writeSectors(cacheEntries[oldUsed].sector,cacheEntries[oldUsed].count,cacheEntries[oldUsed].cache)) return NULL;
|
||||
cacheEntries[oldUsed].dirty = false;
|
||||
}
|
||||
sector = (sector/sectorsPerPage)*sectorsPerPage; // align base sector to page size
|
||||
sec_t next_page = sector + sectorsPerPage;
|
||||
if(next_page > cache->endOfPartition) next_page = cache->endOfPartition;
|
||||
|
||||
if(!cache->disc->readSectors(sector,next_page-sector,cacheEntries[oldUsed].cache)) return NULL;
|
||||
|
||||
cacheEntries[oldUsed].sector = sector;
|
||||
cacheEntries[oldUsed].count = next_page-sector;
|
||||
cacheEntries[oldUsed].last_access = accessTime();
|
||||
|
||||
return &(cacheEntries[oldUsed]);
|
||||
}
|
||||
|
||||
static NTFS_CACHE_ENTRY* _NTFS_cache_findPage(NTFS_CACHE *cache, sec_t sector, sec_t count) {
|
||||
|
||||
unsigned int i;
|
||||
NTFS_CACHE_ENTRY* cacheEntries = cache->cacheEntries;
|
||||
unsigned int numberOfPages = cache->numberOfPages;
|
||||
NTFS_CACHE_ENTRY *entry = NULL;
|
||||
sec_t lowest = UINT_MAX;
|
||||
|
||||
for(i=0;i<numberOfPages;i++) {
|
||||
if (cacheEntries[i].sector != CACHE_FREE) {
|
||||
bool intersect;
|
||||
if (sector > cacheEntries[i].sector) {
|
||||
intersect = sector - cacheEntries[i].sector < cacheEntries[i].count;
|
||||
} else {
|
||||
intersect = cacheEntries[i].sector - sector < count;
|
||||
}
|
||||
|
||||
if ( intersect && (cacheEntries[i].sector < lowest)) {
|
||||
lowest = cacheEntries[i].sector;
|
||||
entry = &cacheEntries[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
bool _NTFS_cache_readSectors(NTFS_CACHE *cache,sec_t sector,sec_t numSectors,void *buffer)
|
||||
{
|
||||
sec_t sec;
|
||||
sec_t secs_to_read;
|
||||
NTFS_CACHE_ENTRY *entry;
|
||||
uint8_t *dest = buffer;
|
||||
|
||||
while(numSectors>0) {
|
||||
entry = _NTFS_cache_getPage(cache,sector);
|
||||
if(entry==NULL) return false;
|
||||
|
||||
sec = sector - entry->sector;
|
||||
secs_to_read = entry->count - sec;
|
||||
if(secs_to_read>numSectors) secs_to_read = numSectors;
|
||||
|
||||
memcpy(dest,entry->cache + (sec*cache->sectorSize),(secs_to_read*cache->sectorSize));
|
||||
|
||||
dest += (secs_to_read*cache->sectorSize);
|
||||
sector += secs_to_read;
|
||||
numSectors -= secs_to_read;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
Reads some data from a cache page, determined by the sector number
|
||||
*/
|
||||
|
||||
bool _NTFS_cache_readPartialSector (NTFS_CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size)
|
||||
{
|
||||
sec_t sec;
|
||||
NTFS_CACHE_ENTRY *entry;
|
||||
|
||||
if (offset + size > cache->sectorSize) return false;
|
||||
|
||||
entry = _NTFS_cache_getPage(cache,sector);
|
||||
if(entry==NULL) return false;
|
||||
|
||||
sec = sector - entry->sector;
|
||||
memcpy(buffer,entry->cache + ((sec*cache->sectorSize) + offset),size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _NTFS_cache_readLittleEndianValue (NTFS_CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes) {
|
||||
uint8_t buf[4];
|
||||
if (!_NTFS_cache_readPartialSector(cache, buf, sector, offset, num_bytes)) return false;
|
||||
|
||||
switch(num_bytes) {
|
||||
case 1: *value = buf[0]; break;
|
||||
case 2: *value = u8array_to_u16(buf,0); break;
|
||||
case 4: *value = u8array_to_u32(buf,0); break;
|
||||
default: return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
Writes some data to a cache page, making sure it is loaded into memory first.
|
||||
*/
|
||||
|
||||
bool _NTFS_cache_writePartialSector (NTFS_CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size)
|
||||
{
|
||||
sec_t sec;
|
||||
NTFS_CACHE_ENTRY *entry;
|
||||
|
||||
if (offset + size > cache->sectorSize) return false;
|
||||
|
||||
entry = _NTFS_cache_getPage(cache,sector);
|
||||
if(entry==NULL) return false;
|
||||
|
||||
sec = sector - entry->sector;
|
||||
memcpy(entry->cache + ((sec*cache->sectorSize) + offset),buffer,size);
|
||||
|
||||
entry->dirty = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _NTFS_cache_writeLittleEndianValue (NTFS_CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset, int size) {
|
||||
uint8_t buf[4] = {0, 0, 0, 0};
|
||||
|
||||
switch(size) {
|
||||
case 1: buf[0] = value; break;
|
||||
case 2: u16_to_u8array(buf, 0, value); break;
|
||||
case 4: u32_to_u8array(buf, 0, value); break;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
return _NTFS_cache_writePartialSector(cache, buf, sector, offset, size);
|
||||
}
|
||||
|
||||
/*
|
||||
Writes some data to a cache page, zeroing out the page first
|
||||
*/
|
||||
|
||||
bool _NTFS_cache_eraseWritePartialSector (NTFS_CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size)
|
||||
{
|
||||
sec_t sec;
|
||||
NTFS_CACHE_ENTRY *entry;
|
||||
|
||||
if (offset + size > cache->sectorSize) return false;
|
||||
|
||||
entry = _NTFS_cache_getPage(cache,sector);
|
||||
if(entry==NULL) return false;
|
||||
|
||||
sec = sector - entry->sector;
|
||||
memset(entry->cache + (sec*cache->sectorSize),0,cache->sectorSize);
|
||||
memcpy(entry->cache + ((sec*cache->sectorSize) + offset),buffer,size);
|
||||
|
||||
entry->dirty = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _NTFS_cache_writeSectors (NTFS_CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer)
|
||||
{
|
||||
sec_t sec;
|
||||
sec_t secs_to_write;
|
||||
NTFS_CACHE_ENTRY* entry;
|
||||
const uint8_t *src = buffer;
|
||||
|
||||
while(numSectors>0)
|
||||
{
|
||||
entry = _NTFS_cache_findPage(cache,sector,numSectors);
|
||||
|
||||
if(entry!=NULL) {
|
||||
|
||||
if ( entry->sector > sector) {
|
||||
|
||||
secs_to_write = entry->sector - sector;
|
||||
|
||||
cache->disc->writeSectors(sector,secs_to_write,src);
|
||||
src += (secs_to_write*cache->sectorSize);
|
||||
sector += secs_to_write;
|
||||
numSectors -= secs_to_write;
|
||||
}
|
||||
|
||||
sec = sector - entry->sector;
|
||||
secs_to_write = entry->count - sec;
|
||||
|
||||
if(secs_to_write>numSectors) secs_to_write = numSectors;
|
||||
|
||||
memcpy(entry->cache + (sec*cache->sectorSize),src,(secs_to_write*cache->sectorSize));
|
||||
|
||||
src += (secs_to_write*cache->sectorSize);
|
||||
sector += secs_to_write;
|
||||
numSectors -= secs_to_write;
|
||||
|
||||
entry->dirty = true;
|
||||
|
||||
} else {
|
||||
cache->disc->writeSectors(sector,numSectors,src);
|
||||
numSectors=0;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
Flushes all dirty pages to disc, clearing the dirty flag.
|
||||
*/
|
||||
bool _NTFS_cache_flush (NTFS_CACHE* cache) {
|
||||
unsigned int i;
|
||||
if(cache==NULL) return true;
|
||||
|
||||
for (i = 0; i < cache->numberOfPages; i++) {
|
||||
if (cache->cacheEntries[i].dirty) {
|
||||
if (!cache->disc->writeSectors (cache->cacheEntries[i].sector, cache->cacheEntries[i].count, cache->cacheEntries[i].cache)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
cache->cacheEntries[i].dirty = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void _NTFS_cache_invalidate (NTFS_CACHE* cache) {
|
||||
unsigned int i;
|
||||
if(cache==NULL)
|
||||
return;
|
||||
|
||||
_NTFS_cache_flush(cache);
|
||||
for (i = 0; i < cache->numberOfPages; i++) {
|
||||
cache->cacheEntries[i].sector = CACHE_FREE;
|
||||
cache->cacheEntries[i].last_access = 0;
|
||||
cache->cacheEntries[i].count = 0;
|
||||
cache->cacheEntries[i].dirty = false;
|
||||
}
|
||||
}
|
135
source/cache2.h
Normal file
135
source/cache2.h
Normal file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
NTFS_CACHE.h
|
||||
The NTFS_CACHE is not visible to the user. It should be flushed
|
||||
when any file is closed or changes are made to the filesystem.
|
||||
|
||||
This NTFS_CACHE implements a least-used-page replacement policy. This will
|
||||
distribute sectors evenly over the pages, so if less than the maximum
|
||||
pages are used at once, they should all eventually remain in the NTFS_CACHE.
|
||||
This also has the benefit of throwing out old sectors, so as not to keep
|
||||
too many stale pages around.
|
||||
|
||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
Copyright (c) 2009 shareese, rodries
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _CACHE2_H
|
||||
#define _CACHE2_H
|
||||
|
||||
//#include "common.h"
|
||||
//#include "disc.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <gctypes.h>
|
||||
#include <ogc/disc_io.h>
|
||||
#include <gccore.h>
|
||||
|
||||
typedef struct {
|
||||
sec_t sector;
|
||||
unsigned int count;
|
||||
u64 last_access;
|
||||
bool dirty;
|
||||
u8* cache;
|
||||
} NTFS_CACHE_ENTRY;
|
||||
|
||||
typedef struct {
|
||||
const DISC_INTERFACE* disc;
|
||||
sec_t endOfPartition;
|
||||
unsigned int numberOfPages;
|
||||
unsigned int sectorsPerPage;
|
||||
sec_t sectorSize;
|
||||
NTFS_CACHE_ENTRY* cacheEntries;
|
||||
} NTFS_CACHE;
|
||||
|
||||
/*
|
||||
Read data from a sector in the NTFS_CACHE
|
||||
If the sector is not in the NTFS_CACHE, it will be swapped in
|
||||
offset is the position to start reading from
|
||||
size is the amount of data to read
|
||||
Precondition: offset + size <= BYTES_PER_READ
|
||||
*/
|
||||
//bool _NTFS_cache_readPartialSector (NTFS_CACHE* NTFS_CACHE, void* buffer, sec_t sector, unsigned int offset, size_t size);
|
||||
|
||||
//bool _NTFS_cache_readLittleEndianValue (NTFS_CACHE* NTFS_CACHE, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes);
|
||||
|
||||
/*
|
||||
Write data to a sector in the NTFS_CACHE
|
||||
If the sector is not in the NTFS_CACHE, it will be swapped in.
|
||||
When the sector is swapped out, the data will be written to the disc
|
||||
offset is the position to start writing to
|
||||
size is the amount of data to write
|
||||
Precondition: offset + size <= BYTES_PER_READ
|
||||
*/
|
||||
//bool _NTFS_cache_writePartialSector (NTFS_CACHE* NTFS_CACHE, const void* buffer, sec_t sector, unsigned int offset, size_t size);
|
||||
|
||||
//bool _NTFS_cache_writeLittleEndianValue (NTFS_CACHE* NTFS_CACHE, const uint32_t value, sec_t sector, unsigned int offset, int num_bytes);
|
||||
|
||||
/*
|
||||
Write data to a sector in the NTFS_CACHE, zeroing the sector first
|
||||
If the sector is not in the NTFS_CACHE, it will be swapped in.
|
||||
When the sector is swapped out, the data will be written to the disc
|
||||
offset is the position to start writing to
|
||||
size is the amount of data to write
|
||||
Precondition: offset + size <= BYTES_PER_READ
|
||||
*/
|
||||
//bool _NTFS_cache_eraseWritePartialSector (NTFS_CACHE* NTFS_CACHE, const void* buffer, sec_t sector, unsigned int offset, size_t size);
|
||||
|
||||
/*
|
||||
Read several sectors from the NTFS_CACHE
|
||||
*/
|
||||
bool _NTFS_cache_readSectors (NTFS_CACHE* NTFS_CACHE, sec_t sector, sec_t numSectors, void* buffer);
|
||||
|
||||
/*
|
||||
Read a full sector from the NTFS_CACHE
|
||||
*/
|
||||
//static inline bool _NTFS_cache_readSector (NTFS_CACHE* NTFS_CACHE, void* buffer, sec_t sector) {
|
||||
// return _NTFS_cache_readPartialSector (NTFS_CACHE, buffer, sector, 0, BYTES_PER_READ);
|
||||
//}
|
||||
|
||||
/*
|
||||
Write a full sector to the NTFS_CACHE
|
||||
*/
|
||||
//static inline bool _NTFS_cache_writeSector (NTFS_CACHE* NTFS_CACHE, const void* buffer, sec_t sector) {
|
||||
// return _NTFS_cache_writePartialSector (NTFS_CACHE, buffer, sector, 0, BYTES_PER_READ);
|
||||
//}
|
||||
|
||||
bool _NTFS_cache_writeSectors (NTFS_CACHE* NTFS_CACHE, sec_t sector, sec_t numSectors, const void* buffer);
|
||||
|
||||
/*
|
||||
Write any dirty sectors back to disc and clear out the contents of the NTFS_CACHE
|
||||
*/
|
||||
bool _NTFS_cache_flush (NTFS_CACHE* NTFS_CACHE);
|
||||
|
||||
/*
|
||||
Clear out the contents of the NTFS_CACHE without writing any dirty sectors first
|
||||
*/
|
||||
void _NTFS_cache_invalidate (NTFS_CACHE* NTFS_CACHE);
|
||||
|
||||
NTFS_CACHE* _NTFS_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition, sec_t sectorSize);
|
||||
|
||||
void _NTFS_cache_destructor (NTFS_CACHE* NTFS_CACHE);
|
||||
|
||||
#endif // _CACHE_H
|
||||
|
271
source/collate.c
Normal file
271
source/collate.c
Normal file
@ -0,0 +1,271 @@
|
||||
/**
|
||||
* collate.c - NTFS collation handling. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2004 Anton Altaparmakov
|
||||
* Copyright (c) 2005 Yura Pakhuchiy
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "attrib.h"
|
||||
#include "index.h"
|
||||
#include "collate.h"
|
||||
#include "debug.h"
|
||||
#include "unistr.h"
|
||||
#include "logging.h"
|
||||
|
||||
/**
|
||||
* ntfs_collate_binary - Which of two binary objects should be listed first
|
||||
* @vol: unused
|
||||
* @data1:
|
||||
* @data1_len:
|
||||
* @data2:
|
||||
* @data2_len:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
static int ntfs_collate_binary(ntfs_volume *vol __attribute__((unused)),
|
||||
const void *data1, const int data1_len,
|
||||
const void *data2, const int data2_len)
|
||||
{
|
||||
int rc;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
rc = memcmp(data1, data2, min(data1_len, data2_len));
|
||||
if (!rc && (data1_len != data2_len)) {
|
||||
if (data1_len < data2_len)
|
||||
rc = -1;
|
||||
else
|
||||
rc = 1;
|
||||
}
|
||||
ntfs_log_trace("Done, returning %i.\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_collate_ntofs_ulong - Which of two long ints should be listed first
|
||||
* @vol: unused
|
||||
* @data1:
|
||||
* @data1_len:
|
||||
* @data2:
|
||||
* @data2_len:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
static int ntfs_collate_ntofs_ulong(ntfs_volume *vol __attribute__((unused)),
|
||||
const void *data1, const int data1_len,
|
||||
const void *data2, const int data2_len)
|
||||
{
|
||||
int rc;
|
||||
u32 d1, d2;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
if (data1_len != data2_len || data1_len != 4) {
|
||||
ntfs_log_error("data1_len or/and data2_len not equal to 4.\n");
|
||||
return NTFS_COLLATION_ERROR;
|
||||
}
|
||||
d1 = le32_to_cpup(data1);
|
||||
d2 = le32_to_cpup(data2);
|
||||
if (d1 < d2)
|
||||
rc = -1;
|
||||
else {
|
||||
if (d1 == d2)
|
||||
rc = 0;
|
||||
else
|
||||
rc = 1;
|
||||
}
|
||||
ntfs_log_trace("Done, returning %i.\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_collate_ntofs_ulongs - Which of two le32 arrays should be listed first
|
||||
*
|
||||
* Returns: -1, 0 or 1 depending of how the arrays compare
|
||||
*/
|
||||
|
||||
static int ntfs_collate_ntofs_ulongs(ntfs_volume *vol __attribute__((unused)),
|
||||
const void *data1, const int data1_len,
|
||||
const void *data2, const int data2_len)
|
||||
{
|
||||
int rc;
|
||||
int len;
|
||||
const le32 *p1, *p2;
|
||||
u32 d1, d2;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
if ((data1_len != data2_len) || (data1_len <= 0) || (data1_len & 3)) {
|
||||
ntfs_log_error("data1_len or data2_len not valid\n");
|
||||
return NTFS_COLLATION_ERROR;
|
||||
}
|
||||
p1 = (const le32*)data1;
|
||||
p2 = (const le32*)data2;
|
||||
len = data1_len;
|
||||
do {
|
||||
d1 = le32_to_cpup(p1);
|
||||
p1++;
|
||||
d2 = le32_to_cpup(p2);
|
||||
p2++;
|
||||
} while ((d1 == d2) && ((len -= 4) > 0));
|
||||
if (d1 < d2)
|
||||
rc = -1;
|
||||
else {
|
||||
if (d1 == d2)
|
||||
rc = 0;
|
||||
else
|
||||
rc = 1;
|
||||
}
|
||||
ntfs_log_trace("Done, returning %i.\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_collate_ntofs_security_hash - Which of two security descriptors
|
||||
* should be listed first
|
||||
* @vol: unused
|
||||
* @data1:
|
||||
* @data1_len:
|
||||
* @data2:
|
||||
* @data2_len:
|
||||
*
|
||||
* JPA compare two security hash keys made of two unsigned le32
|
||||
*
|
||||
* Returns: -1, 0 or 1 depending of how the keys compare
|
||||
*/
|
||||
static int ntfs_collate_ntofs_security_hash(ntfs_volume *vol __attribute__((unused)),
|
||||
const void *data1, const int data1_len,
|
||||
const void *data2, const int data2_len)
|
||||
{
|
||||
int rc;
|
||||
u32 d1, d2;
|
||||
const le32 *p1, *p2;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
if (data1_len != data2_len || data1_len != 8) {
|
||||
ntfs_log_error("data1_len or/and data2_len not equal to 8.\n");
|
||||
return NTFS_COLLATION_ERROR;
|
||||
}
|
||||
p1 = (const le32*)data1;
|
||||
p2 = (const le32*)data2;
|
||||
d1 = le32_to_cpup(p1);
|
||||
d2 = le32_to_cpup(p2);
|
||||
if (d1 < d2)
|
||||
rc = -1;
|
||||
else {
|
||||
if (d1 > d2)
|
||||
rc = 1;
|
||||
else {
|
||||
p1++;
|
||||
p2++;
|
||||
d1 = le32_to_cpup(p1);
|
||||
d2 = le32_to_cpup(p2);
|
||||
if (d1 < d2)
|
||||
rc = -1;
|
||||
else {
|
||||
if (d1 > d2)
|
||||
rc = 1;
|
||||
else
|
||||
rc = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
ntfs_log_trace("Done, returning %i.\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_collate_file_name - Which of two filenames should be listed first
|
||||
* @vol:
|
||||
* @data1:
|
||||
* @data1_len: unused
|
||||
* @data2:
|
||||
* @data2_len: unused
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
static int ntfs_collate_file_name(ntfs_volume *vol,
|
||||
const void *data1, const int data1_len __attribute__((unused)),
|
||||
const void *data2, const int data2_len __attribute__((unused)))
|
||||
{
|
||||
const FILE_NAME_ATTR *file_name_attr1;
|
||||
const FILE_NAME_ATTR *file_name_attr2;
|
||||
int rc;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
file_name_attr1 = (const FILE_NAME_ATTR*)data1;
|
||||
file_name_attr2 = (const FILE_NAME_ATTR*)data2;
|
||||
rc = ntfs_names_full_collate(
|
||||
(ntfschar*)&file_name_attr1->file_name,
|
||||
file_name_attr1->file_name_length,
|
||||
(ntfschar*)&file_name_attr2->file_name,
|
||||
file_name_attr2->file_name_length,
|
||||
CASE_SENSITIVE, vol->upcase, vol->upcase_len);
|
||||
ntfs_log_trace("Done, returning %i.\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a pointer to appropriate collation function.
|
||||
*
|
||||
* Returns NULL if the needed function is not implemented
|
||||
*/
|
||||
|
||||
COLLATE ntfs_get_collate_function(COLLATION_RULES cr)
|
||||
{
|
||||
COLLATE collate;
|
||||
|
||||
switch (cr) {
|
||||
case COLLATION_BINARY :
|
||||
collate = ntfs_collate_binary;
|
||||
break;
|
||||
case COLLATION_FILE_NAME :
|
||||
collate = ntfs_collate_file_name;
|
||||
break;
|
||||
case COLLATION_NTOFS_SECURITY_HASH :
|
||||
collate = ntfs_collate_ntofs_security_hash;
|
||||
break;
|
||||
case COLLATION_NTOFS_ULONG :
|
||||
collate = ntfs_collate_ntofs_ulong;
|
||||
break;
|
||||
case COLLATION_NTOFS_ULONGS :
|
||||
collate = ntfs_collate_ntofs_ulongs;
|
||||
break;
|
||||
default :
|
||||
errno = EOPNOTSUPP;
|
||||
collate = (COLLATE)NULL;
|
||||
break;
|
||||
}
|
||||
return (collate);
|
||||
}
|
34
source/collate.h
Normal file
34
source/collate.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* collate.h - Defines for NTFS collation handling. Originated from the Linux-NTFS
|
||||
* project.
|
||||
*
|
||||
* Copyright (c) 2004 Anton Altaparmakov
|
||||
* Copyright (c) 2005 Yura Pakhuchiy
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_COLLATE_H
|
||||
#define _NTFS_COLLATE_H
|
||||
|
||||
#include "types.h"
|
||||
#include "volume.h"
|
||||
|
||||
#define NTFS_COLLATION_ERROR -2
|
||||
|
||||
extern COLLATE ntfs_get_collate_function(COLLATION_RULES);
|
||||
|
||||
#endif /* _NTFS_COLLATE_H */
|
250
source/compat.c
Normal file
250
source/compat.c
Normal file
@ -0,0 +1,250 @@
|
||||
/**
|
||||
* compat.c - Tweaks for Windows compatibility
|
||||
*
|
||||
* Copyright (c) 2002 Richard Russon
|
||||
* Copyright (c) 2002-2004 Anton Altaparmakov
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
#ifndef HAVE_FFS
|
||||
/**
|
||||
* ffs - Find the first set bit in an int
|
||||
* @x:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
int ffs(int x)
|
||||
{
|
||||
int r = 1;
|
||||
|
||||
if (!x)
|
||||
return 0;
|
||||
if (!(x & 0xffff)) {
|
||||
x >>= 16;
|
||||
r += 16;
|
||||
}
|
||||
if (!(x & 0xff)) {
|
||||
x >>= 8;
|
||||
r += 8;
|
||||
}
|
||||
if (!(x & 0xf)) {
|
||||
x >>= 4;
|
||||
r += 4;
|
||||
}
|
||||
if (!(x & 3)) {
|
||||
x >>= 2;
|
||||
r += 2;
|
||||
}
|
||||
if (!(x & 1)) {
|
||||
x >>= 1;
|
||||
r += 1;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
#endif /* HAVE_FFS */
|
||||
|
||||
#ifndef HAVE_DAEMON
|
||||
/* ************************************************************
|
||||
* From: src.opensolaris.org
|
||||
* src/lib/libresolv2/common/bsd/daemon.c
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 1997-2000 by Sun Microsystems, Inc.
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
#if defined(LIBC_SCCS) && !defined(lint)
|
||||
static const char sccsid[] = "@(#)daemon.c 8.1 (Berkeley) 6/4/93";
|
||||
static const char rcsid[] = "$Id: compat.c,v 1.1.1.1.2.1 2008-08-16 15:17:44 jpandre Exp $";
|
||||
#endif /* LIBC_SCCS and not lint */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1990, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
int daemon(int nochdir, int noclose) {
|
||||
int fd;
|
||||
|
||||
switch (fork()) {
|
||||
case -1:
|
||||
return (-1);
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
if (setsid() == -1)
|
||||
return (-1);
|
||||
|
||||
if (!nochdir)
|
||||
(void)chdir("/");
|
||||
|
||||
if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) {
|
||||
(void)dup2(fd, 0);
|
||||
(void)dup2(fd, 1);
|
||||
(void)dup2(fd, 2);
|
||||
if (fd > 2)
|
||||
(void)close (fd);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
/*
|
||||
* End: src/lib/libresolv2/common/bsd/daemon.c
|
||||
*************************************************************/
|
||||
#endif /* HAVE_DAEMON */
|
||||
|
||||
#ifndef HAVE_STRSEP
|
||||
/* ************************************************************
|
||||
* From: src.opensolaris.org
|
||||
* src/lib/libresolv2/common/bsd/strsep.c
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 1997, by Sun Microsystems, Inc.
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
#if defined(LIBC_SCCS) && !defined(lint)
|
||||
static const char sccsid[] = "strsep.c 8.1 (Berkeley) 6/4/93";
|
||||
static const char rcsid[] = "$Id: compat.c,v 1.1.1.1.2.1 2008-08-16 15:17:44 jpandre Exp $";
|
||||
#endif /* LIBC_SCCS and not lint */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1990, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Get next token from string *stringp, where tokens are possibly-empty
|
||||
* strings separated by characters from delim.
|
||||
*
|
||||
* Writes NULs into the string at *stringp to end tokens.
|
||||
* delim need not remain constant from call to call.
|
||||
* On return, *stringp points past the last NUL written (if there might
|
||||
* be further tokens), or is NULL (if there are definitely no more tokens).
|
||||
*
|
||||
* If *stringp is NULL, strsep returns NULL.
|
||||
*/
|
||||
char *strsep(char **stringp, const char *delim) {
|
||||
char *s;
|
||||
const char *spanp;
|
||||
int c, sc;
|
||||
char *tok;
|
||||
|
||||
if ((s = *stringp) == NULL)
|
||||
return (NULL);
|
||||
for (tok = s;;) {
|
||||
c = *s++;
|
||||
spanp = delim;
|
||||
do {
|
||||
if ((sc = *spanp++) == c) {
|
||||
if (c == 0)
|
||||
s = NULL;
|
||||
else
|
||||
s[-1] = 0;
|
||||
*stringp = s;
|
||||
return (tok);
|
||||
}
|
||||
} while (sc != 0);
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
/*
|
||||
* End: src/lib/libresolv2/common/bsd/strsep.c
|
||||
*************************************************************/
|
||||
#endif /* HAVE_STRSEP */
|
||||
|
86
source/compat.h
Normal file
86
source/compat.h
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* compat.h - Tweaks for Windows compatibility.
|
||||
*
|
||||
* Copyright (c) 2002 Richard Russon
|
||||
* Copyright (c) 2002-2004 Anton Altaparmakov
|
||||
* Copyright (c) 2008-2009 Szabolcs Szakacsits
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_COMPAT_H
|
||||
#define _NTFS_COMPAT_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
#ifdef HAVE_SYS_PARAM_H
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX 4096
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_FFS
|
||||
extern int ffs(int i);
|
||||
#endif /* HAVE_FFS */
|
||||
|
||||
#ifndef HAVE_DAEMON
|
||||
extern int daemon(int nochdir, int noclose);
|
||||
#endif /* HAVE_DAEMON */
|
||||
|
||||
#ifndef HAVE_STRSEP
|
||||
extern char *strsep(char **stringp, const char *delim);
|
||||
#endif /* HAVE_STRSEP */
|
||||
|
||||
#ifdef WINDOWS
|
||||
|
||||
#define HAVE_STDIO_H /* mimic config.h */
|
||||
#define HAVE_STDARG_H
|
||||
|
||||
#define atoll _atoi64
|
||||
#define fdatasync commit
|
||||
#define __inline__ inline
|
||||
#define __attribute__(X) /*nothing*/
|
||||
|
||||
#else /* !defined WINDOWS */
|
||||
|
||||
#ifndef O_BINARY
|
||||
#define O_BINARY 0 /* unix is binary by default */
|
||||
#endif
|
||||
|
||||
#ifdef GEKKO
|
||||
|
||||
#include "mem_allocate.h"
|
||||
|
||||
#define XATTR_CREATE 1
|
||||
#define XATTR_REPLACE 2
|
||||
|
||||
#define MINORBITS 20
|
||||
#define MINORMASK ((1U << MINORBITS) - 1)
|
||||
|
||||
#define major(dev) ((unsigned int) ((dev) >> MINORBITS))
|
||||
#define minor(dev) ((unsigned int) ((dev) & MINORMASK))
|
||||
#define mkdev(ma,mi) (((ma) << MINORBITS) | (mi))
|
||||
#define random rand
|
||||
|
||||
#endif /* defined GEKKO */
|
||||
|
||||
#endif /* defined WINDOWS */
|
||||
|
||||
#endif /* defined _NTFS_COMPAT_H */
|
||||
|
1831
source/compress.c
Normal file
1831
source/compress.c
Normal file
File diff suppressed because it is too large
Load Diff
41
source/compress.h
Normal file
41
source/compress.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* compress.h - Exports for compressed attribute handling.
|
||||
* Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2004 Anton Altaparmakov
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_COMPRESS_H
|
||||
#define _NTFS_COMPRESS_H
|
||||
|
||||
#include "types.h"
|
||||
#include "attrib.h"
|
||||
|
||||
extern s64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count,
|
||||
void *b);
|
||||
|
||||
extern s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *brl, s64 wpos,
|
||||
s64 offs, s64 to_write, s64 rounded,
|
||||
const void *b, int compressed_part,
|
||||
VCN *update_from);
|
||||
|
||||
extern int ntfs_compressed_close(ntfs_attr *na, runlist_element *brl,
|
||||
s64 offs, VCN *update_from);
|
||||
|
||||
#endif /* defined _NTFS_COMPRESS_H */
|
||||
|
374
source/config.h
Normal file
374
source/config.h
Normal file
@ -0,0 +1,374 @@
|
||||
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* Define to 1 if debug should be enabled */
|
||||
#undef ENABLE_DEBUG
|
||||
|
||||
/* Define to 1 if the nfconv patch should be enabled */
|
||||
#undef ENABLE_NFCONV
|
||||
|
||||
/* Define to 1 if using internal fuse */
|
||||
#undef FUSE_INTERNAL
|
||||
|
||||
/* Define to 1 if you have the `atexit' function. */
|
||||
#define HAVE_ATEXIT 1
|
||||
|
||||
/* Define to 1 if you have the `basename' function. */
|
||||
#undef HAVE_BASENAME
|
||||
|
||||
/* Define to 1 if you have the <byteswap.h> header file. */
|
||||
#undef HAVE_BYTESWAP_H
|
||||
|
||||
/* Define to 1 if you have the `clock_gettime' function. */
|
||||
#undef HAVE_CLOCK_GETTIME
|
||||
|
||||
/* Define to 1 if you have the <ctype.h> header file. */
|
||||
#define HAVE_CTYPE_H 1
|
||||
|
||||
/* Define to 1 if you have the `daemon' function. */
|
||||
#undef HAVE_DAEMON
|
||||
|
||||
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||
#undef HAVE_DLFCN_H
|
||||
|
||||
/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
|
||||
#undef HAVE_DOPRNT
|
||||
|
||||
/* Define to 1 if you have the `dup2' function. */
|
||||
#undef HAVE_DUP2
|
||||
|
||||
/* Define to 1 if you have the <endian.h> header file. */
|
||||
#undef HAVE_ENDIAN_H
|
||||
|
||||
/* Define to 1 if you have the <errno.h> header file. */
|
||||
#define HAVE_ERRNO_H 1
|
||||
|
||||
/* Define to 1 if you have the <fcntl.h> header file. */
|
||||
#define HAVE_FCNTL_H 1
|
||||
|
||||
/* Define to 1 if you have the `fdatasync' function. */
|
||||
#undef HAVE_FDATASYNC
|
||||
|
||||
/* Define to 1 if you have the <features.h> header file. */
|
||||
#undef HAVE_FEATURES_H
|
||||
|
||||
/* Define to 1 if you have the `ffs' function. */
|
||||
#define HAVE_FFS 1
|
||||
|
||||
/* Define to 1 if you have the `fork' function. */
|
||||
#define HAVE_FORK 1
|
||||
|
||||
/* Define to 1 if you have the `getmntent' function. */
|
||||
#undef HAVE_GETMNTENT
|
||||
|
||||
/* Define to 1 if you have the <getopt.h> header file. */
|
||||
#define HAVE_GETOPT_H 1
|
||||
|
||||
/* Define to 1 if you have the `getopt_long' function. */
|
||||
#define HAVE_GETOPT_LONG 1
|
||||
|
||||
/* Define to 1 if you have the `gettimeofday' function. */
|
||||
#undef HAVE_GETTIMEOFDAY
|
||||
|
||||
/* Define to 1 if you have the `hasmntopt' function. */
|
||||
#undef HAVE_HASMNTOPT
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#define HAVE_INTTYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the <libgen.h> header file. */
|
||||
#define HAVE_LIBGEN_H 1
|
||||
|
||||
/* Define to 1 if you have the <libintl.h> header file. */
|
||||
#undef HAVE_LIBINTL_H
|
||||
|
||||
/* Define to 1 if you have the <limits.h> header file. */
|
||||
#define HAVE_LIMITS_H 1
|
||||
|
||||
/* Define to 1 if you have the <linux/fd.h> header file. */
|
||||
#undef HAVE_LINUX_FD_H
|
||||
|
||||
/* Define to 1 if you have the <linux/hdreg.h> header file. */
|
||||
#undef HAVE_LINUX_HDREG_H
|
||||
|
||||
/* Define to 1 if you have the <linux/major.h> header file. */
|
||||
#undef HAVE_LINUX_MAJOR_H
|
||||
|
||||
/* Define to 1 if you have the <locale.h> header file. */
|
||||
#define HAVE_LOCALE_H 1
|
||||
|
||||
/* Define to 1 if you have the <machine/endian.h> header file. */
|
||||
#define HAVE_MACHINE_ENDIAN_H 1
|
||||
|
||||
/* Define to 1 if you have the <math.h> header file. */
|
||||
#define HAVE_MATH_H 1
|
||||
|
||||
/* Define to 1 if mbrtowc and mbstate_t are properly declared. */
|
||||
#define HAVE_MBRTOWC 1
|
||||
|
||||
/* Define to 1 if you have the `mbsinit' function. */
|
||||
#define HAVE_MBSINIT 1
|
||||
|
||||
/* Define to 1 if you have the `memmove' function. */
|
||||
#define HAVE_MEMMOVE 1
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#undef HAVE_MEMORY_H
|
||||
|
||||
/* Define to 1 if you have the `memset' function. */
|
||||
#define HAVE_MEMSET 1
|
||||
|
||||
/* Define to 1 if you have the <mntent.h> header file. */
|
||||
#undef HAVE_MNTENT_H
|
||||
|
||||
/* Define to 1 if you have the `realpath' function. */
|
||||
#undef HAVE_REALPATH
|
||||
|
||||
/* Define to 1 if you have the `regcomp' function. */
|
||||
#undef HAVE_REGCOMP
|
||||
|
||||
/* Define to 1 if you have the `setlocale' function. */
|
||||
#define HAVE_SETLOCALE 1
|
||||
|
||||
/* Define to 1 if you have the `setxattr' function. */
|
||||
#undef HAVE_SETXATTR
|
||||
|
||||
/* Define to 1 if `stat' has the bug that it succeeds when given the
|
||||
zero-length file name argument. */
|
||||
#define HAVE_STAT_EMPTY_STRING_BUG 1
|
||||
|
||||
/* Define to 1 if you have the <stdarg.h> header file. */
|
||||
#define HAVE_STDARG_H 1
|
||||
|
||||
/* Define to 1 if stdbool.h conforms to C99. */
|
||||
#define HAVE_STDBOOL_H 1
|
||||
|
||||
/* Define to 1 if you have the <stddef.h> header file. */
|
||||
#define HAVE_STDDEF_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#define HAVE_STDINT_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdio.h> header file. */
|
||||
#define HAVE_STDIO_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#define HAVE_STDLIB_H 1
|
||||
|
||||
/* Define to 1 if you have the `strcasecmp' function. */
|
||||
#define HAVE_STRCASECMP 1
|
||||
|
||||
/* Define to 1 if you have the `strchr' function. */
|
||||
#define HAVE_STRCHR 1
|
||||
|
||||
/* Define to 1 if you have the `strdup' function. */
|
||||
#define HAVE_STRDUP 1
|
||||
|
||||
/* Define to 1 if you have the `strerror' function. */
|
||||
#define HAVE_STRERROR 1
|
||||
|
||||
/* Define to 1 if you have the `strftime' function. */
|
||||
#define HAVE_STRFTIME 1
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#undef HAVE_STRINGS_H
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#define HAVE_STRING_H 1
|
||||
|
||||
/* Define to 1 if you have the `strnlen' function. */
|
||||
#define HAVE_STRNLEN 1
|
||||
|
||||
/* Define to 1 if you have the `strsep' function. */
|
||||
#define HAVE_STRSEP 1
|
||||
|
||||
/* Define to 1 if you have the `strtol' function. */
|
||||
#define HAVE_STRTOL 1
|
||||
|
||||
/* Define to 1 if you have the `strtoul' function. */
|
||||
#define HAVE_STRTOUL 1
|
||||
|
||||
/* Define to 1 if `st_atim' is member of `struct stat'. */
|
||||
#undef HAVE_STRUCT_STAT_ST_ATIM
|
||||
|
||||
/* Define to 1 if `st_atimensec' is member of `struct stat'. */
|
||||
#undef HAVE_STRUCT_STAT_ST_ATIMENSEC
|
||||
|
||||
/* Define to 1 if `st_atimespec' is member of `struct stat'. */
|
||||
#undef HAVE_STRUCT_STAT_ST_ATIMESPEC
|
||||
|
||||
/* Define to 1 if `st_blocks' is member of `struct stat'. */
|
||||
#undef HAVE_STRUCT_STAT_ST_BLOCKS
|
||||
|
||||
/* Define to 1 if `st_rdev' is member of `struct stat'. */
|
||||
#undef HAVE_STRUCT_STAT_ST_RDEV
|
||||
|
||||
/* Define to 1 if your `struct stat' has `st_blocks'. Deprecated, use
|
||||
`HAVE_STRUCT_STAT_ST_BLOCKS' instead. */
|
||||
#undef HAVE_ST_BLOCKS
|
||||
|
||||
/* Define to 1 if you have the `sysconf' function. */
|
||||
#define HAVE_SYSCONF 1
|
||||
|
||||
/* Define to 1 if you have the <syslog.h> header file. */
|
||||
#undef HAVE_SYSLOG_H
|
||||
|
||||
/* Define to 1 if you have the <sys/byteorder.h> header file. */
|
||||
#undef HAVE_SYS_BYTEORDER_H
|
||||
|
||||
/* Define to 1 if you have the <sys/endian.h> header file. */
|
||||
#undef HAVE_SYS_ENDIAN_H
|
||||
|
||||
/* Define to 1 if you have the <sys/ioctl.h> header file. */
|
||||
#undef HAVE_SYS_IOCTL_H
|
||||
|
||||
/* Define to 1 if you have the <sys/mkdev.h> header file. */
|
||||
#undef HAVE_SYS_MKDEV_H
|
||||
|
||||
/* Define to 1 if you have the <sys/mount.h> header file. */
|
||||
#undef HAVE_SYS_MOUNT_H
|
||||
|
||||
/* Define to 1 if you have the <sys/param.h> header file. */
|
||||
#define HAVE_SYS_PARAM_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/statvfs.h> header file. */
|
||||
#define HAVE_SYS_STATVFS_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#define HAVE_SYS_STAT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/sysmacros.h> header file. */
|
||||
#undef HAVE_SYS_SYSMACROS_H
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/vfs.h> header file. */
|
||||
#undef HAVE_SYS_VFS_H
|
||||
|
||||
/* Define to 1 if you have the <time.h> header file. */
|
||||
#define HAVE_TIME_H 1
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#define HAVE_UNISTD_H 1
|
||||
|
||||
/* Define to 1 if you have the `utime' function. */
|
||||
#undef HAVE_UTIME
|
||||
|
||||
/* Define to 1 if you have the `utimensat' function. */
|
||||
#undef HAVE_UTIMENSAT
|
||||
|
||||
/* Define to 1 if you have the <utime.h> header file. */
|
||||
#define HAVE_UTIME_H 1
|
||||
|
||||
/* Define to 1 if `utime(file, NULL)' sets file's timestamp to the present. */
|
||||
#undef HAVE_UTIME_NULL
|
||||
|
||||
/* Define to 1 if you have the `vprintf' function. */
|
||||
#define HAVE_VPRINTF 1
|
||||
|
||||
/* Define to 1 if you have the <wchar.h> header file. */
|
||||
#define HAVE_WCHAR_H 1
|
||||
|
||||
/* Define to 1 if you have the <windows.h> header file. */
|
||||
#undef HAVE_WINDOWS_H
|
||||
|
||||
/* Define to 1 if the system has the type `_Bool'. */
|
||||
#undef HAVE__BOOL
|
||||
|
||||
/* Don't update /etc/mtab */
|
||||
#undef IGNORE_MTAB
|
||||
|
||||
/* Define to 1 if `lstat' dereferences a symlink specified with a trailing
|
||||
slash. */
|
||||
#undef LSTAT_FOLLOWS_SLASHED_SYMLINK
|
||||
|
||||
/* Define to 1 if your C compiler doesn't accept -c and -o together. */
|
||||
#undef NO_MINUS_C_MINUS_O
|
||||
|
||||
/* Don't use default IO ops */
|
||||
#undef NO_NTFS_DEVICE_DEFAULT_IO_OPS
|
||||
|
||||
/* Name of package */
|
||||
#define PACKAGE "ntfs-3g"
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net"
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#define PACKAGE_NAME "ntfs-3g"
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#define PACKAGE_STRING "ntfs-3g 2010.8.8"
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#define PACKAGE_TARNAME "ntfs-3g"
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#define PACKAGE_VERSION "2010.8.8"
|
||||
|
||||
/* POSIX ACL support */
|
||||
#undef POSIXACLS
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#define STDC_HEADERS 1
|
||||
|
||||
/* Enable extensions on AIX 3, Interix. */
|
||||
#ifndef _ALL_SOURCE
|
||||
# define _ALL_SOURCE 1
|
||||
#endif
|
||||
/* Enable GNU extensions on systems that have them. */
|
||||
#ifndef _GNU_SOURCE
|
||||
# define _GNU_SOURCE 1
|
||||
#endif
|
||||
/* Enable threading extensions on Solaris. */
|
||||
#ifndef _POSIX_PTHREAD_SEMANTICS
|
||||
# define _POSIX_PTHREAD_SEMANTICS 1
|
||||
#endif
|
||||
/* Enable extensions on HP NonStop. */
|
||||
#ifndef _TANDEM_SOURCE
|
||||
# define _TANDEM_SOURCE 1
|
||||
#endif
|
||||
/* Enable general extensions on Solaris. */
|
||||
#ifndef __EXTENSIONS__
|
||||
# define __EXTENSIONS__ 1
|
||||
#endif
|
||||
|
||||
/* Version number of package */
|
||||
#define VERSION "2010.8.8"
|
||||
|
||||
/* Define to 1 if this is a Windows OS */
|
||||
#undef WINDOWS
|
||||
|
||||
/* Define to 1 if your processor stores words with the most significant byte
|
||||
first (like Motorola and SPARC, unlike Intel and VAX). */
|
||||
#define WORDS_BIGENDIAN 1
|
||||
|
||||
/* Define to 1 if your processor stores words with the least significant byte
|
||||
first (like Intel and VAX, unlike Motorola and SPARC). */
|
||||
#undef WORDS_LITTLEENDIAN
|
||||
|
||||
/* Number of bits in a file offset, on hosts where this is settable. */
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
/* Enable GNU extensions on systems that have them. */
|
||||
#ifndef _GNU_SOURCE
|
||||
# undef _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
/* Define for large files, on AIX-style hosts. */
|
||||
#define _LARGE_FILES 1
|
||||
|
||||
/* Required define if using POSIX threads */
|
||||
#undef _REENTRANT
|
||||
|
||||
/* Define to `__inline__' or `__inline' if that's what the C compiler
|
||||
calls it, or to nothing if 'inline' is not supported under any name. */
|
||||
#ifndef __cplusplus
|
||||
#define inline __inline__
|
||||
#endif
|
||||
|
||||
/* Define to `long int' if <sys/types.h> does not define. */
|
||||
#undef off_t
|
||||
|
||||
/* Define to `unsigned int' if <sys/types.h> does not define. */
|
||||
#undef size_t
|
79
source/debug.c
Normal file
79
source/debug.c
Normal file
@ -0,0 +1,79 @@
|
||||
/**
|
||||
* debug.c - Debugging output functions. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2002-2004 Anton Altaparmakov
|
||||
* Copyright (c) 2004-2006 Szabolcs Szakacsits
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "runlist.h"
|
||||
#include "debug.h"
|
||||
#include "logging.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
/**
|
||||
* ntfs_debug_runlist_dump - Dump a runlist.
|
||||
* @rl:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
void ntfs_debug_runlist_dump(const runlist_element *rl)
|
||||
{
|
||||
int i = 0;
|
||||
const char *lcn_str[5] = { "LCN_HOLE ", "LCN_RL_NOT_MAPPED",
|
||||
"LCN_ENOENT ", "LCN_EINVAL ",
|
||||
"LCN_unknown " };
|
||||
|
||||
ntfs_log_debug("NTFS-fs DEBUG: Dumping runlist (values in hex):\n");
|
||||
if (!rl) {
|
||||
ntfs_log_debug("Run list not present.\n");
|
||||
return;
|
||||
}
|
||||
ntfs_log_debug("VCN LCN Run length\n");
|
||||
do {
|
||||
LCN lcn = (rl + i)->lcn;
|
||||
|
||||
if (lcn < (LCN)0) {
|
||||
int idx = -lcn - 1;
|
||||
|
||||
if (idx > -LCN_EINVAL - 1)
|
||||
idx = 4;
|
||||
ntfs_log_debug("%-16lld %s %-16lld%s\n",
|
||||
(long long)rl[i].vcn, lcn_str[idx],
|
||||
(long long)rl[i].length,
|
||||
rl[i].length ? "" : " (runlist end)");
|
||||
} else
|
||||
ntfs_log_debug("%-16lld %-16lld %-16lld%s\n",
|
||||
(long long)rl[i].vcn, (long long)rl[i].lcn,
|
||||
(long long)rl[i].length,
|
||||
rl[i].length ? "" : " (runlist end)");
|
||||
} while (rl[i++].length);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
47
source/debug.h
Normal file
47
source/debug.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* debug.h - Debugging output functions. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2002-2004 Anton Altaparmakov
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_DEBUG_H
|
||||
#define _NTFS_DEBUG_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "logging.h"
|
||||
|
||||
struct _runlist_element;
|
||||
|
||||
#ifdef DEBUG
|
||||
extern void ntfs_debug_runlist_dump(const struct _runlist_element *rl);
|
||||
#else
|
||||
static __inline__ void ntfs_debug_runlist_dump(const struct _runlist_element *rl __attribute__((unused))) {}
|
||||
#endif
|
||||
|
||||
#define NTFS_BUG(msg) \
|
||||
{ \
|
||||
int ___i; \
|
||||
ntfs_log_critical("Bug in %s(): %s\n", __FUNCTION__, msg); \
|
||||
ntfs_log_debug("Forcing segmentation fault!"); \
|
||||
___i = ((int*)NULL)[1]; \
|
||||
}
|
||||
|
||||
#endif /* defined _NTFS_DEBUG_H */
|
730
source/device.c
Normal file
730
source/device.c
Normal file
@ -0,0 +1,730 @@
|
||||
/**
|
||||
* device.c - Low level device io functions. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2004-2006 Anton Altaparmakov
|
||||
* Copyright (c) 2004-2006 Szabolcs Szakacsits
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_IOCTL_H
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_PARAM_H
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_MOUNT_H
|
||||
#include <sys/mount.h>
|
||||
#endif
|
||||
#ifdef HAVE_LINUX_FD_H
|
||||
#include <linux/fd.h>
|
||||
#endif
|
||||
#ifdef HAVE_LINUX_HDREG_H
|
||||
#include <linux/hdreg.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "mst.h"
|
||||
#include "debug.h"
|
||||
#include "device.h"
|
||||
#include "logging.h"
|
||||
#include "misc.h"
|
||||
|
||||
#if defined(linux) && defined(_IO) && !defined(BLKGETSIZE)
|
||||
#define BLKGETSIZE _IO(0x12,96) /* Get device size in 512-byte blocks. */
|
||||
#endif
|
||||
#if defined(linux) && defined(_IOR) && !defined(BLKGETSIZE64)
|
||||
#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* Get device size in bytes. */
|
||||
#endif
|
||||
#if defined(linux) && !defined(HDIO_GETGEO)
|
||||
#define HDIO_GETGEO 0x0301 /* Get device geometry. */
|
||||
#endif
|
||||
#if defined(linux) && defined(_IO) && !defined(BLKSSZGET)
|
||||
# define BLKSSZGET _IO(0x12,104) /* Get device sector size in bytes. */
|
||||
#endif
|
||||
#if defined(linux) && defined(_IO) && !defined(BLKBSZSET)
|
||||
# define BLKBSZSET _IOW(0x12,113,size_t) /* Set device block size in bytes. */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ntfs_device_alloc - allocate an ntfs device structure and pre-initialize it
|
||||
* @name: name of the device (must be present)
|
||||
* @state: initial device state (usually zero)
|
||||
* @dops: ntfs device operations to use with the device (must be present)
|
||||
* @priv_data: pointer to private data (optional)
|
||||
*
|
||||
* Allocate an ntfs device structure and pre-initialize it with the user-
|
||||
* specified device operations @dops, device state @state, device name @name,
|
||||
* and optional private data @priv_data.
|
||||
*
|
||||
* Note, @name is copied and can hence be freed after this functions returns.
|
||||
*
|
||||
* On success return a pointer to the allocated ntfs device structure and on
|
||||
* error return NULL with errno set to the error code returned by ntfs_malloc().
|
||||
*/
|
||||
struct ntfs_device *ntfs_device_alloc(const char *name, const long state,
|
||||
struct ntfs_device_operations *dops, void *priv_data)
|
||||
{
|
||||
struct ntfs_device *dev;
|
||||
|
||||
if (!name) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev = ntfs_malloc(sizeof(struct ntfs_device));
|
||||
if (dev) {
|
||||
if (!(dev->d_name = strdup(name))) {
|
||||
int eo = errno;
|
||||
free(dev);
|
||||
errno = eo;
|
||||
return NULL;
|
||||
}
|
||||
dev->d_ops = dops;
|
||||
dev->d_state = state;
|
||||
dev->d_private = priv_data;
|
||||
}
|
||||
return dev;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_free - free an ntfs device structure
|
||||
* @dev: ntfs device structure to free
|
||||
*
|
||||
* Free the ntfs device structure @dev.
|
||||
*
|
||||
* Return 0 on success or -1 on error with errno set to the error code. The
|
||||
* following error codes are defined:
|
||||
* EINVAL Invalid pointer @dev.
|
||||
* EBUSY Device is still open. Close it before freeing it!
|
||||
*/
|
||||
int ntfs_device_free(struct ntfs_device *dev)
|
||||
{
|
||||
if (!dev) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (NDevOpen(dev)) {
|
||||
errno = EBUSY;
|
||||
return -1;
|
||||
}
|
||||
free(dev->d_name);
|
||||
free(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_pread - positioned read from disk
|
||||
* @dev: device to read from
|
||||
* @pos: position in device to read from
|
||||
* @count: number of bytes to read
|
||||
* @b: output data buffer
|
||||
*
|
||||
* This function will read @count bytes from device @dev at position @pos into
|
||||
* the data buffer @b.
|
||||
*
|
||||
* On success, return the number of successfully read bytes. If this number is
|
||||
* lower than @count this means that we have either reached end of file or
|
||||
* encountered an error during the read so that the read is partial. 0 means
|
||||
* end of file or nothing to read (@count is 0).
|
||||
*
|
||||
* On error and nothing has been read, return -1 with errno set appropriately
|
||||
* to the return code of either seek, read, or set to EINVAL in case of
|
||||
* invalid arguments.
|
||||
*/
|
||||
s64 ntfs_pread(struct ntfs_device *dev, const s64 pos, s64 count, void *b)
|
||||
{
|
||||
s64 br, total;
|
||||
struct ntfs_device_operations *dops;
|
||||
|
||||
ntfs_log_trace("pos %lld, count %lld\n",(long long)pos,(long long)count);
|
||||
|
||||
if (!b || count < 0 || pos < 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (!count)
|
||||
return 0;
|
||||
|
||||
dops = dev->d_ops;
|
||||
|
||||
for (total = 0; count; count -= br, total += br) {
|
||||
br = dops->pread(dev, (char*)b + total, count, pos + total);
|
||||
/* If everything ok, continue. */
|
||||
if (br > 0)
|
||||
continue;
|
||||
/* If EOF or error return number of bytes read. */
|
||||
if (!br || total)
|
||||
return total;
|
||||
/* Nothing read and error, return error status. */
|
||||
return br;
|
||||
}
|
||||
/* Finally, return the number of bytes read. */
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_pwrite - positioned write to disk
|
||||
* @dev: device to write to
|
||||
* @pos: position in file descriptor to write to
|
||||
* @count: number of bytes to write
|
||||
* @b: data buffer to write to disk
|
||||
*
|
||||
* This function will write @count bytes from data buffer @b to the device @dev
|
||||
* at position @pos.
|
||||
*
|
||||
* On success, return the number of successfully written bytes. If this number
|
||||
* is lower than @count this means that the write has been interrupted in
|
||||
* flight or that an error was encountered during the write so that the write
|
||||
* is partial. 0 means nothing was written (also return 0 when @count is 0).
|
||||
*
|
||||
* On error and nothing has been written, return -1 with errno set
|
||||
* appropriately to the return code of either seek, write, or set
|
||||
* to EINVAL in case of invalid arguments.
|
||||
*/
|
||||
s64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count,
|
||||
const void *b)
|
||||
{
|
||||
s64 written, total, ret = -1;
|
||||
struct ntfs_device_operations *dops;
|
||||
|
||||
ntfs_log_trace("pos %lld, count %lld\n",(long long)pos,(long long)count);
|
||||
|
||||
if (!b || count < 0 || pos < 0) {
|
||||
errno = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (!count)
|
||||
return 0;
|
||||
if (NDevReadOnly(dev)) {
|
||||
errno = EROFS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dops = dev->d_ops;
|
||||
|
||||
NDevSetDirty(dev);
|
||||
for (total = 0; count; count -= written, total += written) {
|
||||
written = dops->pwrite(dev, (const char*)b + total, count,
|
||||
pos + total);
|
||||
/* If everything ok, continue. */
|
||||
if (written > 0)
|
||||
continue;
|
||||
/*
|
||||
* If nothing written or error return number of bytes written.
|
||||
*/
|
||||
if (!written || total)
|
||||
break;
|
||||
/* Nothing written and error, return error status. */
|
||||
total = written;
|
||||
break;
|
||||
}
|
||||
ret = total;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_mst_pread - multi sector transfer (mst) positioned read
|
||||
* @dev: device to read from
|
||||
* @pos: position in file descriptor to read from
|
||||
* @count: number of blocks to read
|
||||
* @bksize: size of each block that needs mst deprotecting
|
||||
* @b: output data buffer
|
||||
*
|
||||
* Multi sector transfer (mst) positioned read. This function will read @count
|
||||
* blocks of size @bksize bytes each from device @dev at position @pos into the
|
||||
* the data buffer @b.
|
||||
*
|
||||
* On success, return the number of successfully read blocks. If this number is
|
||||
* lower than @count this means that we have reached end of file, that the read
|
||||
* was interrupted, or that an error was encountered during the read so that
|
||||
* the read is partial. 0 means end of file or nothing was read (also return 0
|
||||
* when @count or @bksize are 0).
|
||||
*
|
||||
* On error and nothing was read, return -1 with errno set appropriately to the
|
||||
* return code of either seek, read, or set to EINVAL in case of invalid
|
||||
* arguments.
|
||||
*
|
||||
* NOTE: If an incomplete multi sector transfer has been detected the magic
|
||||
* will have been changed to magic_BAAD but no error will be returned. Thus it
|
||||
* is possible that we return count blocks as being read but that any number
|
||||
* (between zero and count!) of these blocks is actually subject to a multi
|
||||
* sector transfer error. This should be detected by the caller by checking for
|
||||
* the magic being "BAAD".
|
||||
*/
|
||||
s64 ntfs_mst_pread(struct ntfs_device *dev, const s64 pos, s64 count,
|
||||
const u32 bksize, void *b)
|
||||
{
|
||||
s64 br, i;
|
||||
|
||||
if (bksize & (bksize - 1) || bksize % NTFS_BLOCK_SIZE) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
/* Do the read. */
|
||||
br = ntfs_pread(dev, pos, count * bksize, b);
|
||||
if (br < 0)
|
||||
return br;
|
||||
/*
|
||||
* Apply fixups to successfully read data, disregarding any errors
|
||||
* returned from the MST fixup function. This is because we want to
|
||||
* fixup everything possible and we rely on the fact that the "BAAD"
|
||||
* magic will be detected later on.
|
||||
*/
|
||||
count = br / bksize;
|
||||
for (i = 0; i < count; ++i)
|
||||
ntfs_mst_post_read_fixup((NTFS_RECORD*)
|
||||
((u8*)b + i * bksize), bksize);
|
||||
/* Finally, return the number of complete blocks read. */
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_mst_pwrite - multi sector transfer (mst) positioned write
|
||||
* @dev: device to write to
|
||||
* @pos: position in file descriptor to write to
|
||||
* @count: number of blocks to write
|
||||
* @bksize: size of each block that needs mst protecting
|
||||
* @b: data buffer to write to disk
|
||||
*
|
||||
* Multi sector transfer (mst) positioned write. This function will write
|
||||
* @count blocks of size @bksize bytes each from data buffer @b to the device
|
||||
* @dev at position @pos.
|
||||
*
|
||||
* On success, return the number of successfully written blocks. If this number
|
||||
* is lower than @count this means that the write has been interrupted or that
|
||||
* an error was encountered during the write so that the write is partial. 0
|
||||
* means nothing was written (also return 0 when @count or @bksize are 0).
|
||||
*
|
||||
* On error and nothing has been written, return -1 with errno set
|
||||
* appropriately to the return code of either seek, write, or set
|
||||
* to EINVAL in case of invalid arguments.
|
||||
*
|
||||
* NOTE: We mst protect the data, write it, then mst deprotect it using a quick
|
||||
* deprotect algorithm (no checking). This saves us from making a copy before
|
||||
* the write and at the same time causes the usn to be incremented in the
|
||||
* buffer. This conceptually fits in better with the idea that cached data is
|
||||
* always deprotected and protection is performed when the data is actually
|
||||
* going to hit the disk and the cache is immediately deprotected again
|
||||
* simulating an mst read on the written data. This way cache coherency is
|
||||
* achieved.
|
||||
*/
|
||||
s64 ntfs_mst_pwrite(struct ntfs_device *dev, const s64 pos, s64 count,
|
||||
const u32 bksize, void *b)
|
||||
{
|
||||
s64 written, i;
|
||||
|
||||
if (count < 0 || bksize % NTFS_BLOCK_SIZE) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (!count)
|
||||
return 0;
|
||||
/* Prepare data for writing. */
|
||||
for (i = 0; i < count; ++i) {
|
||||
int err;
|
||||
|
||||
err = ntfs_mst_pre_write_fixup((NTFS_RECORD*)
|
||||
((u8*)b + i * bksize), bksize);
|
||||
if (err < 0) {
|
||||
/* Abort write at this position. */
|
||||
if (!i)
|
||||
return err;
|
||||
count = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Write the prepared data. */
|
||||
written = ntfs_pwrite(dev, pos, count * bksize, b);
|
||||
/* Quickly deprotect the data again. */
|
||||
for (i = 0; i < count; ++i)
|
||||
ntfs_mst_post_write_fixup((NTFS_RECORD*)((u8*)b + i * bksize));
|
||||
if (written <= 0)
|
||||
return written;
|
||||
/* Finally, return the number of complete blocks written. */
|
||||
return written / bksize;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_cluster_read - read ntfs clusters
|
||||
* @vol: volume to read from
|
||||
* @lcn: starting logical cluster number
|
||||
* @count: number of clusters to read
|
||||
* @b: output data buffer
|
||||
*
|
||||
* Read @count ntfs clusters starting at logical cluster number @lcn from
|
||||
* volume @vol into buffer @b. Return number of clusters read or -1 on error,
|
||||
* with errno set to the error code.
|
||||
*/
|
||||
s64 ntfs_cluster_read(const ntfs_volume *vol, const s64 lcn, const s64 count,
|
||||
void *b)
|
||||
{
|
||||
s64 br;
|
||||
|
||||
if (!vol || lcn < 0 || count < 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (vol->nr_clusters < lcn + count) {
|
||||
errno = ESPIPE;
|
||||
ntfs_log_perror("Trying to read outside of volume "
|
||||
"(%lld < %lld)", (long long)vol->nr_clusters,
|
||||
(long long)lcn + count);
|
||||
return -1;
|
||||
}
|
||||
br = ntfs_pread(vol->dev, lcn << vol->cluster_size_bits,
|
||||
count << vol->cluster_size_bits, b);
|
||||
if (br < 0) {
|
||||
ntfs_log_perror("Error reading cluster(s)");
|
||||
return br;
|
||||
}
|
||||
return br >> vol->cluster_size_bits;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_cluster_write - write ntfs clusters
|
||||
* @vol: volume to write to
|
||||
* @lcn: starting logical cluster number
|
||||
* @count: number of clusters to write
|
||||
* @b: data buffer to write to disk
|
||||
*
|
||||
* Write @count ntfs clusters starting at logical cluster number @lcn from
|
||||
* buffer @b to volume @vol. Return the number of clusters written or -1 on
|
||||
* error, with errno set to the error code.
|
||||
*/
|
||||
s64 ntfs_cluster_write(const ntfs_volume *vol, const s64 lcn,
|
||||
const s64 count, const void *b)
|
||||
{
|
||||
s64 bw;
|
||||
|
||||
if (!vol || lcn < 0 || count < 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (vol->nr_clusters < lcn + count) {
|
||||
errno = ESPIPE;
|
||||
ntfs_log_perror("Trying to write outside of volume "
|
||||
"(%lld < %lld)", (long long)vol->nr_clusters,
|
||||
(long long)lcn + count);
|
||||
return -1;
|
||||
}
|
||||
if (!NVolReadOnly(vol))
|
||||
bw = ntfs_pwrite(vol->dev, lcn << vol->cluster_size_bits,
|
||||
count << vol->cluster_size_bits, b);
|
||||
else
|
||||
bw = count << vol->cluster_size_bits;
|
||||
if (bw < 0) {
|
||||
ntfs_log_perror("Error writing cluster(s)");
|
||||
return bw;
|
||||
}
|
||||
return bw >> vol->cluster_size_bits;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_offset_valid - test if a device offset is valid
|
||||
* @dev: open device
|
||||
* @ofs: offset to test for validity
|
||||
*
|
||||
* Test if the offset @ofs is an existing location on the device described
|
||||
* by the open device structure @dev.
|
||||
*
|
||||
* Return 0 if it is valid and -1 if it is not valid.
|
||||
*/
|
||||
static int ntfs_device_offset_valid(struct ntfs_device *dev, s64 ofs)
|
||||
{
|
||||
char ch;
|
||||
|
||||
if (dev->d_ops->seek(dev, ofs, SEEK_SET) >= 0 &&
|
||||
dev->d_ops->read(dev, &ch, 1) == 1)
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_size_get - return the size of a device in blocks
|
||||
* @dev: open device
|
||||
* @block_size: block size in bytes in which to return the result
|
||||
*
|
||||
* Return the number of @block_size sized blocks in the device described by the
|
||||
* open device @dev.
|
||||
*
|
||||
* Adapted from e2fsutils-1.19, Copyright (C) 1995 Theodore Ts'o.
|
||||
*
|
||||
* On error return -1 with errno set to the error code.
|
||||
*/
|
||||
s64 ntfs_device_size_get(struct ntfs_device *dev, int block_size)
|
||||
{
|
||||
s64 high, low;
|
||||
|
||||
if (!dev || block_size <= 0 || (block_size - 1) & block_size) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
#ifdef BLKGETSIZE64
|
||||
{ u64 size;
|
||||
|
||||
if (dev->d_ops->ioctl(dev, BLKGETSIZE64, &size) >= 0) {
|
||||
ntfs_log_debug("BLKGETSIZE64 nr bytes = %llu (0x%llx)\n",
|
||||
(unsigned long long)size,
|
||||
(unsigned long long)size);
|
||||
return (s64)size / block_size;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef BLKGETSIZE
|
||||
{ unsigned long size;
|
||||
|
||||
if (dev->d_ops->ioctl(dev, BLKGETSIZE, &size) >= 0) {
|
||||
ntfs_log_debug("BLKGETSIZE nr 512 byte blocks = %lu (0x%lx)\n",
|
||||
size, size);
|
||||
return (s64)size * 512 / block_size;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef FDGETPRM
|
||||
{ struct floppy_struct this_floppy;
|
||||
|
||||
if (dev->d_ops->ioctl(dev, FDGETPRM, &this_floppy) >= 0) {
|
||||
ntfs_log_debug("FDGETPRM nr 512 byte blocks = %lu (0x%lx)\n",
|
||||
(unsigned long)this_floppy.size,
|
||||
(unsigned long)this_floppy.size);
|
||||
return (s64)this_floppy.size * 512 / block_size;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* We couldn't figure it out by using a specialized ioctl,
|
||||
* so do binary search to find the size of the device.
|
||||
*/
|
||||
low = 0LL;
|
||||
for (high = 1024LL; !ntfs_device_offset_valid(dev, high); high <<= 1)
|
||||
low = high;
|
||||
while (low < high - 1LL) {
|
||||
const s64 mid = (low + high) / 2;
|
||||
|
||||
if (!ntfs_device_offset_valid(dev, mid))
|
||||
low = mid;
|
||||
else
|
||||
high = mid;
|
||||
}
|
||||
dev->d_ops->seek(dev, 0LL, SEEK_SET);
|
||||
return (low + 1LL) / block_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_partition_start_sector_get - get starting sector of a partition
|
||||
* @dev: open device
|
||||
*
|
||||
* On success, return the starting sector of the partition @dev in the parent
|
||||
* block device of @dev. On error return -1 with errno set to the error code.
|
||||
*
|
||||
* The following error codes are defined:
|
||||
* EINVAL Input parameter error
|
||||
* EOPNOTSUPP System does not support HDIO_GETGEO ioctl
|
||||
* ENOTTY @dev is a file or a device not supporting HDIO_GETGEO
|
||||
*/
|
||||
s64 ntfs_device_partition_start_sector_get(struct ntfs_device *dev)
|
||||
{
|
||||
if (!dev) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
#ifdef HDIO_GETGEO
|
||||
{ struct hd_geometry geo;
|
||||
|
||||
if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) {
|
||||
ntfs_log_debug("HDIO_GETGEO start_sect = %lu (0x%lx)\n",
|
||||
geo.start, geo.start);
|
||||
return geo.start;
|
||||
}
|
||||
}
|
||||
#else
|
||||
errno = EOPNOTSUPP;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_heads_get - get number of heads of device
|
||||
* @dev: open device
|
||||
*
|
||||
* On success, return the number of heads on the device @dev. On error return
|
||||
* -1 with errno set to the error code.
|
||||
*
|
||||
* The following error codes are defined:
|
||||
* EINVAL Input parameter error
|
||||
* EOPNOTSUPP System does not support HDIO_GETGEO ioctl
|
||||
* ENOTTY @dev is a file or a device not supporting HDIO_GETGEO
|
||||
*/
|
||||
int ntfs_device_heads_get(struct ntfs_device *dev)
|
||||
{
|
||||
if (!dev) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
#ifdef HDIO_GETGEO
|
||||
{ struct hd_geometry geo;
|
||||
|
||||
if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) {
|
||||
ntfs_log_debug("HDIO_GETGEO heads = %u (0x%x)\n",
|
||||
(unsigned)geo.heads,
|
||||
(unsigned)geo.heads);
|
||||
return geo.heads;
|
||||
}
|
||||
}
|
||||
#else
|
||||
errno = EOPNOTSUPP;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_sectors_per_track_get - get number of sectors per track of device
|
||||
* @dev: open device
|
||||
*
|
||||
* On success, return the number of sectors per track on the device @dev. On
|
||||
* error return -1 with errno set to the error code.
|
||||
*
|
||||
* The following error codes are defined:
|
||||
* EINVAL Input parameter error
|
||||
* EOPNOTSUPP System does not support HDIO_GETGEO ioctl
|
||||
* ENOTTY @dev is a file or a device not supporting HDIO_GETGEO
|
||||
*/
|
||||
int ntfs_device_sectors_per_track_get(struct ntfs_device *dev)
|
||||
{
|
||||
if (!dev) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
#ifdef HDIO_GETGEO
|
||||
{ struct hd_geometry geo;
|
||||
|
||||
if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) {
|
||||
ntfs_log_debug("HDIO_GETGEO sectors_per_track = %u (0x%x)\n",
|
||||
(unsigned)geo.sectors,
|
||||
(unsigned)geo.sectors);
|
||||
return geo.sectors;
|
||||
}
|
||||
}
|
||||
#else
|
||||
errno = EOPNOTSUPP;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_sector_size_get - get sector size of a device
|
||||
* @dev: open device
|
||||
*
|
||||
* On success, return the sector size in bytes of the device @dev.
|
||||
* On error return -1 with errno set to the error code.
|
||||
*
|
||||
* The following error codes are defined:
|
||||
* EINVAL Input parameter error
|
||||
* EOPNOTSUPP System does not support BLKSSZGET ioctl
|
||||
* ENOTTY @dev is a file or a device not supporting BLKSSZGET
|
||||
*/
|
||||
int ntfs_device_sector_size_get(struct ntfs_device *dev)
|
||||
{
|
||||
if (!dev) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
#ifdef BLKSSZGET
|
||||
{
|
||||
int sect_size = 0;
|
||||
|
||||
if (!dev->d_ops->ioctl(dev, BLKSSZGET, §_size)) {
|
||||
ntfs_log_debug("BLKSSZGET sector size = %d bytes\n",
|
||||
sect_size);
|
||||
return sect_size;
|
||||
}
|
||||
}
|
||||
#else
|
||||
errno = EOPNOTSUPP;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_block_size_set - set block size of a device
|
||||
* @dev: open device
|
||||
* @block_size: block size to set @dev to
|
||||
*
|
||||
* On success, return 0.
|
||||
* On error return -1 with errno set to the error code.
|
||||
*
|
||||
* The following error codes are defined:
|
||||
* EINVAL Input parameter error
|
||||
* EOPNOTSUPP System does not support BLKBSZSET ioctl
|
||||
* ENOTTY @dev is a file or a device not supporting BLKBSZSET
|
||||
*/
|
||||
int ntfs_device_block_size_set(struct ntfs_device *dev,
|
||||
int block_size __attribute__((unused)))
|
||||
{
|
||||
if (!dev) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
#ifdef BLKBSZSET
|
||||
{
|
||||
size_t s_block_size = block_size;
|
||||
if (!dev->d_ops->ioctl(dev, BLKBSZSET, &s_block_size)) {
|
||||
ntfs_log_debug("Used BLKBSZSET to set block size to "
|
||||
"%d bytes.\n", block_size);
|
||||
return 0;
|
||||
}
|
||||
/* If not a block device, pretend it was successful. */
|
||||
if (!NDevBlock(dev))
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
/* If not a block device, pretend it was successful. */
|
||||
if (!NDevBlock(dev))
|
||||
return 0;
|
||||
errno = EOPNOTSUPP;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
128
source/device.h
Normal file
128
source/device.h
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* device.h - Exports for low level device io. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2006 Anton Altaparmakov
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_DEVICE_H
|
||||
#define _NTFS_DEVICE_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "device_io.h"
|
||||
#include "types.h"
|
||||
#include "support.h"
|
||||
#include "volume.h"
|
||||
|
||||
/**
|
||||
* enum ntfs_device_state_bits -
|
||||
*
|
||||
* Defined bits for the state field in the ntfs_device structure.
|
||||
*/
|
||||
typedef enum {
|
||||
ND_Open, /* 1: Device is open. */
|
||||
ND_ReadOnly, /* 1: Device is read-only. */
|
||||
ND_Dirty, /* 1: Device is dirty, needs sync. */
|
||||
ND_Block, /* 1: Device is a block device. */
|
||||
} ntfs_device_state_bits;
|
||||
|
||||
#define test_ndev_flag(nd, flag) test_bit(ND_##flag, (nd)->d_state)
|
||||
#define set_ndev_flag(nd, flag) set_bit(ND_##flag, (nd)->d_state)
|
||||
#define clear_ndev_flag(nd, flag) clear_bit(ND_##flag, (nd)->d_state)
|
||||
|
||||
#define NDevOpen(nd) test_ndev_flag(nd, Open)
|
||||
#define NDevSetOpen(nd) set_ndev_flag(nd, Open)
|
||||
#define NDevClearOpen(nd) clear_ndev_flag(nd, Open)
|
||||
|
||||
#define NDevReadOnly(nd) test_ndev_flag(nd, ReadOnly)
|
||||
#define NDevSetReadOnly(nd) set_ndev_flag(nd, ReadOnly)
|
||||
#define NDevClearReadOnly(nd) clear_ndev_flag(nd, ReadOnly)
|
||||
|
||||
#define NDevDirty(nd) test_ndev_flag(nd, Dirty)
|
||||
#define NDevSetDirty(nd) set_ndev_flag(nd, Dirty)
|
||||
#define NDevClearDirty(nd) clear_ndev_flag(nd, Dirty)
|
||||
|
||||
#define NDevBlock(nd) test_ndev_flag(nd, Block)
|
||||
#define NDevSetBlock(nd) set_ndev_flag(nd, Block)
|
||||
#define NDevClearBlock(nd) clear_ndev_flag(nd, Block)
|
||||
|
||||
/**
|
||||
* struct ntfs_device -
|
||||
*
|
||||
* The ntfs device structure defining all operations needed to access the low
|
||||
* level device underlying the ntfs volume.
|
||||
*/
|
||||
struct ntfs_device {
|
||||
struct ntfs_device_operations *d_ops; /* Device operations. */
|
||||
unsigned long d_state; /* State of the device. */
|
||||
char *d_name; /* Name of device. */
|
||||
void *d_private; /* Private data used by the
|
||||
device operations. */
|
||||
};
|
||||
|
||||
struct stat;
|
||||
|
||||
/**
|
||||
* struct ntfs_device_operations -
|
||||
*
|
||||
* The ntfs device operations defining all operations that can be performed on
|
||||
* the low level device described by an ntfs device structure.
|
||||
*/
|
||||
struct ntfs_device_operations {
|
||||
int (*open)(struct ntfs_device *dev, int flags);
|
||||
int (*close)(struct ntfs_device *dev);
|
||||
s64 (*seek)(struct ntfs_device *dev, s64 offset, int whence);
|
||||
s64 (*read)(struct ntfs_device *dev, void *buf, s64 count);
|
||||
s64 (*write)(struct ntfs_device *dev, const void *buf, s64 count);
|
||||
s64 (*pread)(struct ntfs_device *dev, void *buf, s64 count, s64 offset);
|
||||
s64 (*pwrite)(struct ntfs_device *dev, const void *buf, s64 count,
|
||||
s64 offset);
|
||||
int (*sync)(struct ntfs_device *dev);
|
||||
int (*stat)(struct ntfs_device *dev, struct stat *buf);
|
||||
int (*ioctl)(struct ntfs_device *dev, int request, void *argp);
|
||||
};
|
||||
|
||||
extern struct ntfs_device *ntfs_device_alloc(const char *name, const long state,
|
||||
struct ntfs_device_operations *dops, void *priv_data);
|
||||
extern int ntfs_device_free(struct ntfs_device *dev);
|
||||
|
||||
extern s64 ntfs_pread(struct ntfs_device *dev, const s64 pos, s64 count,
|
||||
void *b);
|
||||
extern s64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count,
|
||||
const void *b);
|
||||
|
||||
extern s64 ntfs_mst_pread(struct ntfs_device *dev, const s64 pos, s64 count,
|
||||
const u32 bksize, void *b);
|
||||
extern s64 ntfs_mst_pwrite(struct ntfs_device *dev, const s64 pos, s64 count,
|
||||
const u32 bksize, void *b);
|
||||
|
||||
extern s64 ntfs_cluster_read(const ntfs_volume *vol, const s64 lcn,
|
||||
const s64 count, void *b);
|
||||
extern s64 ntfs_cluster_write(const ntfs_volume *vol, const s64 lcn,
|
||||
const s64 count, const void *b);
|
||||
|
||||
extern s64 ntfs_device_size_get(struct ntfs_device *dev, int block_size);
|
||||
extern s64 ntfs_device_partition_start_sector_get(struct ntfs_device *dev);
|
||||
extern int ntfs_device_heads_get(struct ntfs_device *dev);
|
||||
extern int ntfs_device_sectors_per_track_get(struct ntfs_device *dev);
|
||||
extern int ntfs_device_sector_size_get(struct ntfs_device *dev);
|
||||
extern int ntfs_device_block_size_set(struct ntfs_device *dev, int block_size);
|
||||
|
||||
#endif /* defined _NTFS_DEVICE_H */
|
40
source/device_io.c
Normal file
40
source/device_io.c
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* device_io.c - Default device io operations. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2003 Anton Altaparmakov
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifndef GEKKO
|
||||
#ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS
|
||||
|
||||
#ifndef __CYGWIN32__
|
||||
|
||||
/* Not on Cygwin; use standard Unix style low level device operations. */
|
||||
#include "unix_io.c"
|
||||
|
||||
#else /* __CYGWIN32__ */
|
||||
|
||||
/* On Cygwin; use Win32 low level device operations. */
|
||||
#include "win32_io.c"
|
||||
|
||||
#endif /* __CYGWIN32__ */
|
||||
|
||||
#endif /* NO_NTFS_DEVICE_DEFAULT_IO_OPS */
|
||||
#endif /* GEKKO */
|
82
source/device_io.h
Normal file
82
source/device_io.h
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* device_io.h - Exports for default device io. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2006 Anton Altaparmakov
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_DEVICE_IO_H
|
||||
#define _NTFS_DEVICE_IO_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS
|
||||
|
||||
#ifndef __CYGWIN32__
|
||||
|
||||
#ifndef GEKKO
|
||||
/* Not on Cygwin; use standard Unix style low level device operations. */
|
||||
#define ntfs_device_default_io_ops ntfs_device_unix_io_ops
|
||||
#else
|
||||
/* Wii i/o device. */
|
||||
#define ntfs_device_default_io_ops ntfs_device_gekko_io_ops
|
||||
#endif
|
||||
|
||||
#else /* __CYGWIN32__ */
|
||||
|
||||
#ifndef HDIO_GETGEO
|
||||
# define HDIO_GETGEO 0x301
|
||||
/**
|
||||
* struct hd_geometry -
|
||||
*/
|
||||
struct hd_geometry {
|
||||
unsigned char heads;
|
||||
unsigned char sectors;
|
||||
unsigned short cylinders;
|
||||
unsigned long start;
|
||||
};
|
||||
#endif
|
||||
#ifndef BLKGETSIZE
|
||||
# define BLKGETSIZE 0x1260
|
||||
#endif
|
||||
#ifndef BLKSSZGET
|
||||
# define BLKSSZGET 0x1268
|
||||
#endif
|
||||
#ifndef BLKGETSIZE64
|
||||
# define BLKGETSIZE64 0x80041272
|
||||
#endif
|
||||
#ifndef BLKBSZSET
|
||||
# define BLKBSZSET 0x40041271
|
||||
#endif
|
||||
|
||||
/* On Cygwin; use Win32 low level device operations. */
|
||||
#define ntfs_device_default_io_ops ntfs_device_win32_io_ops
|
||||
|
||||
#endif /* __CYGWIN32__ */
|
||||
|
||||
|
||||
/* Forward declaration. */
|
||||
struct ntfs_device_operations;
|
||||
|
||||
extern struct ntfs_device_operations ntfs_device_default_io_ops;
|
||||
|
||||
#endif /* NO_NTFS_DEVICE_DEFAULT_IO_OPS */
|
||||
|
||||
#endif /* defined _NTFS_DEVICE_IO_H */
|
||||
|
2660
source/dir.c
Normal file
2660
source/dir.c
Normal file
File diff suppressed because it is too large
Load Diff
128
source/dir.h
Normal file
128
source/dir.h
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* dir.h - Exports for directory handling. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2002 Anton Altaparmakov
|
||||
* Copyright (c) 2005-2006 Yura Pakhuchiy
|
||||
* Copyright (c) 2004-2005 Richard Russon
|
||||
* Copyright (c) 2005-2008 Szabolcs Szakacsits
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_DIR_H
|
||||
#define _NTFS_DIR_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define PATH_SEP '/'
|
||||
|
||||
/*
|
||||
* We do not have these under DJGPP, so define our version that do not conflict
|
||||
* with other S_IFs defined under DJGPP.
|
||||
*/
|
||||
#ifdef DJGPP
|
||||
#ifndef S_IFLNK
|
||||
#define S_IFLNK 0120000
|
||||
#endif
|
||||
#ifndef S_ISLNK
|
||||
#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
|
||||
#endif
|
||||
#ifndef S_IFSOCK
|
||||
#define S_IFSOCK 0140000
|
||||
#endif
|
||||
#ifndef S_ISSOCK
|
||||
#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The little endian Unicode strings $I30, $SII, $SDH, $O, $Q, $R
|
||||
* as a global constant.
|
||||
*/
|
||||
extern ntfschar NTFS_INDEX_I30[5];
|
||||
extern ntfschar NTFS_INDEX_SII[5];
|
||||
extern ntfschar NTFS_INDEX_SDH[5];
|
||||
extern ntfschar NTFS_INDEX_O[3];
|
||||
extern ntfschar NTFS_INDEX_Q[3];
|
||||
extern ntfschar NTFS_INDEX_R[3];
|
||||
|
||||
extern u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni,
|
||||
const ntfschar *uname, const int uname_len);
|
||||
extern u64 ntfs_inode_lookup_by_mbsname(ntfs_inode *dir_ni, const char *name);
|
||||
extern void ntfs_inode_update_mbsname(ntfs_inode *dir_ni, const char *name,
|
||||
u64 inum);
|
||||
|
||||
extern ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent,
|
||||
const char *pathname);
|
||||
extern ntfs_inode *ntfs_create(ntfs_inode *dir_ni, le32 securid,
|
||||
ntfschar *name, u8 name_len, mode_t type);
|
||||
extern ntfs_inode *ntfs_create_device(ntfs_inode *dir_ni, le32 securid,
|
||||
ntfschar *name, u8 name_len, mode_t type, dev_t dev);
|
||||
extern ntfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni, le32 securid,
|
||||
ntfschar *name, u8 name_len, ntfschar *target, int target_len);
|
||||
extern int ntfs_check_empty_dir(ntfs_inode *ni);
|
||||
extern int ntfs_delete(ntfs_volume *vol, const char *path,
|
||||
ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name,
|
||||
u8 name_len);
|
||||
|
||||
extern int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name,
|
||||
u8 name_len);
|
||||
|
||||
/*
|
||||
* File types (adapted from include <linux/fs.h>)
|
||||
*/
|
||||
#define NTFS_DT_UNKNOWN 0
|
||||
#define NTFS_DT_FIFO 1
|
||||
#define NTFS_DT_CHR 2
|
||||
#define NTFS_DT_DIR 4
|
||||
#define NTFS_DT_BLK 6
|
||||
#define NTFS_DT_REG 8
|
||||
#define NTFS_DT_LNK 10
|
||||
#define NTFS_DT_SOCK 12
|
||||
#define NTFS_DT_WHT 14
|
||||
|
||||
/*
|
||||
* This is the "ntfs_filldir" function type, used by ntfs_readdir() to let
|
||||
* the caller specify what kind of dirent layout it wants to have.
|
||||
* This allows the caller to read directories into their application or
|
||||
* to have different dirent layouts depending on the binary type.
|
||||
*/
|
||||
typedef int (*ntfs_filldir_t)(void *dirent, const ntfschar *name,
|
||||
const int name_len, const int name_type, const s64 pos,
|
||||
const MFT_REF mref, const unsigned dt_type);
|
||||
|
||||
extern int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos,
|
||||
void *dirent, ntfs_filldir_t filldir);
|
||||
|
||||
ntfs_inode *ntfs_dir_parent_inode(ntfs_inode *ni);
|
||||
|
||||
int ntfs_get_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni,
|
||||
char *value, size_t size);
|
||||
int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni,
|
||||
const char *value, size_t size, int flags);
|
||||
int ntfs_remove_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni);
|
||||
|
||||
#if CACHE_INODE_SIZE
|
||||
|
||||
struct CACHED_GENERIC;
|
||||
|
||||
extern int ntfs_dir_inode_hash(const struct CACHED_GENERIC *cached);
|
||||
extern int ntfs_dir_lookup_hash(const struct CACHED_GENERIC *cached);
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* defined _NTFS_DIR_H */
|
||||
|
439
source/efs.c
Normal file
439
source/efs.c
Normal file
@ -0,0 +1,439 @@
|
||||
/**
|
||||
* efs.c - Limited processing of encrypted files
|
||||
*
|
||||
* This module is part of ntfs-3g library
|
||||
*
|
||||
* Copyright (c) 2009 Martin Bene
|
||||
* Copyright (c) 2009-2010 Jean-Pierre Andre
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SETXATTR
|
||||
#include <sys/xattr.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_SYSMACROS_H
|
||||
#include <sys/sysmacros.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "debug.h"
|
||||
#include "attrib.h"
|
||||
#include "inode.h"
|
||||
#include "dir.h"
|
||||
#include "efs.h"
|
||||
#include "index.h"
|
||||
#include "logging.h"
|
||||
#include "misc.h"
|
||||
#include "efs.h"
|
||||
|
||||
#ifdef HAVE_SETXATTR /* extended attributes interface required */
|
||||
|
||||
static ntfschar logged_utility_stream_name[] = {
|
||||
const_cpu_to_le16('$'),
|
||||
const_cpu_to_le16('E'),
|
||||
const_cpu_to_le16('F'),
|
||||
const_cpu_to_le16('S'),
|
||||
const_cpu_to_le16(0)
|
||||
} ;
|
||||
|
||||
|
||||
/*
|
||||
* Get the ntfs EFS info into an extended attribute
|
||||
*/
|
||||
|
||||
int ntfs_get_efs_info(ntfs_inode *ni, char *value, size_t size)
|
||||
{
|
||||
EFS_ATTR_HEADER *efs_info;
|
||||
s64 attr_size = 0;
|
||||
|
||||
if (ni) {
|
||||
if (ni->flags & FILE_ATTR_ENCRYPTED) {
|
||||
efs_info = (EFS_ATTR_HEADER*)ntfs_attr_readall(ni,
|
||||
AT_LOGGED_UTILITY_STREAM,(ntfschar*)NULL, 0,
|
||||
&attr_size);
|
||||
if (efs_info
|
||||
&& (le32_to_cpu(efs_info->length) == attr_size)) {
|
||||
if (attr_size <= (s64)size) {
|
||||
if (value)
|
||||
memcpy(value,efs_info,attr_size);
|
||||
else {
|
||||
errno = EFAULT;
|
||||
attr_size = 0;
|
||||
}
|
||||
} else
|
||||
if (size) {
|
||||
errno = ERANGE;
|
||||
attr_size = 0;
|
||||
}
|
||||
free (efs_info);
|
||||
} else {
|
||||
if (efs_info) {
|
||||
free(efs_info);
|
||||
ntfs_log_error("Bad efs_info for inode %lld\n",
|
||||
(long long)ni->mft_no);
|
||||
} else {
|
||||
ntfs_log_error("Could not get efsinfo"
|
||||
" for inode %lld\n",
|
||||
(long long)ni->mft_no);
|
||||
}
|
||||
errno = EIO;
|
||||
attr_size = 0;
|
||||
}
|
||||
} else {
|
||||
errno = ENODATA;
|
||||
ntfs_log_trace("Inode %lld is not encrypted\n",
|
||||
(long long)ni->mft_no);
|
||||
}
|
||||
}
|
||||
return (attr_size ? (int)attr_size : -errno);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fix all encrypted AT_DATA attributes of an inode
|
||||
*
|
||||
* The fix may require making an attribute non resident, which
|
||||
* requires more space in the MFT record, and may cause some
|
||||
* attribute to be expelled and the full record to be reorganized.
|
||||
* When this happens, the search for data attributes has to be
|
||||
* reinitialized.
|
||||
*
|
||||
* Returns zero if successful.
|
||||
* -1 if there is a problem.
|
||||
*/
|
||||
|
||||
static int fixup_loop(ntfs_inode *ni)
|
||||
{
|
||||
ntfs_attr_search_ctx *ctx;
|
||||
ntfs_attr *na;
|
||||
ATTR_RECORD *a;
|
||||
BOOL restart;
|
||||
BOOL first;
|
||||
int cnt;
|
||||
int maxcnt;
|
||||
int res = 0;
|
||||
|
||||
maxcnt = 0;
|
||||
do {
|
||||
restart = FALSE;
|
||||
ctx = ntfs_attr_get_search_ctx(ni, NULL);
|
||||
if (!ctx) {
|
||||
ntfs_log_error("Failed to get ctx for efs\n");
|
||||
res = -1;
|
||||
}
|
||||
cnt = 0;
|
||||
while (!restart && !res
|
||||
&& !ntfs_attr_lookup(AT_DATA, NULL, 0,
|
||||
CASE_SENSITIVE, 0, NULL, 0, ctx)) {
|
||||
cnt++;
|
||||
a = ctx->attr;
|
||||
na = ntfs_attr_open(ctx->ntfs_ino, AT_DATA,
|
||||
(ntfschar*)((u8*)a + le16_to_cpu(a->name_offset)),
|
||||
a->name_length);
|
||||
if (!na) {
|
||||
ntfs_log_error("can't open DATA Attribute\n");
|
||||
res = -1;
|
||||
}
|
||||
if (na && !(ctx->attr->flags & ATTR_IS_ENCRYPTED)) {
|
||||
if (!NAttrNonResident(na)
|
||||
&& ntfs_attr_make_non_resident(na, ctx)) {
|
||||
/*
|
||||
* ntfs_attr_make_non_resident fails if there
|
||||
* is not enough space in the MFT record.
|
||||
* When this happens, force making non-resident
|
||||
* so that some other attribute is expelled.
|
||||
*/
|
||||
if (ntfs_attr_force_non_resident(na)) {
|
||||
res = -1;
|
||||
} else {
|
||||
/* make sure there is some progress */
|
||||
if (cnt <= maxcnt) {
|
||||
errno = EIO;
|
||||
ntfs_log_error("Multiple failure"
|
||||
" making non resident\n");
|
||||
res = -1;
|
||||
} else {
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
ctx = (ntfs_attr_search_ctx*)NULL;
|
||||
restart = TRUE;
|
||||
maxcnt = cnt;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!restart && !res
|
||||
&& ntfs_efs_fixup_attribute(ctx, na)) {
|
||||
ntfs_log_error("Error in efs fixup of AT_DATA Attribute\n");
|
||||
res = -1;
|
||||
}
|
||||
}
|
||||
if (na)
|
||||
ntfs_attr_close(na);
|
||||
}
|
||||
first = FALSE;
|
||||
} while (restart && !res);
|
||||
if (ctx)
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
return (res);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the efs data from an extended attribute
|
||||
* Warning : the new data is not checked
|
||||
* Returns 0, or -1 if there is a problem
|
||||
*/
|
||||
|
||||
int ntfs_set_efs_info(ntfs_inode *ni, const char *value, size_t size,
|
||||
int flags)
|
||||
|
||||
{
|
||||
int res;
|
||||
int written;
|
||||
ntfs_attr *na;
|
||||
const EFS_ATTR_HEADER *info_header;
|
||||
|
||||
res = 0;
|
||||
if (ni && value && size) {
|
||||
if (ni->flags & (FILE_ATTR_ENCRYPTED | FILE_ATTR_COMPRESSED)) {
|
||||
if (ni->flags & FILE_ATTR_ENCRYPTED) {
|
||||
ntfs_log_trace("Inode %lld already encrypted\n",
|
||||
(long long)ni->mft_no);
|
||||
errno = EEXIST;
|
||||
} else {
|
||||
/*
|
||||
* Possible problem : if encrypted file was
|
||||
* restored in a compressed directory, it was
|
||||
* restored as compressed.
|
||||
* TODO : decompress first.
|
||||
*/
|
||||
ntfs_log_error("Inode %lld cannot be encrypted and compressed\n",
|
||||
(long long)ni->mft_no);
|
||||
errno = EIO;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
info_header = (const EFS_ATTR_HEADER*)value;
|
||||
/* make sure we get a likely efsinfo */
|
||||
if (le32_to_cpu(info_header->length) != size) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
if (!ntfs_attr_exist(ni,AT_LOGGED_UTILITY_STREAM,
|
||||
(ntfschar*)NULL,0)) {
|
||||
if (!(flags & XATTR_REPLACE)) {
|
||||
/*
|
||||
* no logged_utility_stream attribute : add one,
|
||||
* apparently, this does not feed the new value in
|
||||
*/
|
||||
res = ntfs_attr_add(ni,AT_LOGGED_UTILITY_STREAM,
|
||||
logged_utility_stream_name,4,
|
||||
(u8*)NULL,(s64)size);
|
||||
} else {
|
||||
errno = ENODATA;
|
||||
res = -1;
|
||||
}
|
||||
} else {
|
||||
errno = EEXIST;
|
||||
res = -1;
|
||||
}
|
||||
if (!res) {
|
||||
/*
|
||||
* open and update the existing efs data
|
||||
*/
|
||||
na = ntfs_attr_open(ni, AT_LOGGED_UTILITY_STREAM,
|
||||
logged_utility_stream_name, 4);
|
||||
if (na) {
|
||||
/* resize attribute */
|
||||
res = ntfs_attr_truncate(na, (s64)size);
|
||||
/* overwrite value if any */
|
||||
if (!res && value) {
|
||||
written = (int)ntfs_attr_pwrite(na,
|
||||
(s64)0, (s64)size, value);
|
||||
if (written != (s64)size) {
|
||||
ntfs_log_error("Failed to "
|
||||
"update efs data\n");
|
||||
errno = EIO;
|
||||
res = -1;
|
||||
}
|
||||
}
|
||||
ntfs_attr_close(na);
|
||||
} else
|
||||
res = -1;
|
||||
}
|
||||
if (!res) {
|
||||
/* Don't handle AT_DATA Attribute(s) if inode is a directory */
|
||||
if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) {
|
||||
/* iterate over AT_DATA attributes */
|
||||
/* set encrypted flag, truncate attribute to match padding bytes */
|
||||
|
||||
if (fixup_loop(ni))
|
||||
return -1;
|
||||
}
|
||||
ni->flags |= FILE_ATTR_ENCRYPTED;
|
||||
NInoSetDirty(ni);
|
||||
NInoFileNameSetDirty(ni);
|
||||
}
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
res = -1;
|
||||
}
|
||||
return (res ? -1 : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fixup raw encrypted AT_DATA Attribute
|
||||
* read padding length from last two bytes
|
||||
* truncate attribute, make non-resident,
|
||||
* set data size to match padding length
|
||||
* set ATTR_IS_ENCRYPTED flag on attribute
|
||||
*
|
||||
* Return 0 if successful
|
||||
* -1 if failed (errno tells why)
|
||||
*/
|
||||
|
||||
int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na)
|
||||
{
|
||||
u64 newsize;
|
||||
u64 oldsize;
|
||||
le16 appended_bytes;
|
||||
u16 padding_length;
|
||||
ntfs_inode *ni;
|
||||
BOOL close_ctx = FALSE;
|
||||
|
||||
if (!na) {
|
||||
ntfs_log_error("no na specified for efs_fixup_attribute\n");
|
||||
goto err_out;
|
||||
}
|
||||
if (!ctx) {
|
||||
ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
|
||||
if (!ctx) {
|
||||
ntfs_log_error("Failed to get ctx for efs\n");
|
||||
goto err_out;
|
||||
}
|
||||
close_ctx = TRUE;
|
||||
if (ntfs_attr_lookup(AT_DATA, na->name, na->name_len,
|
||||
CASE_SENSITIVE, 0, NULL, 0, ctx)) {
|
||||
ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n");
|
||||
goto err_out;
|
||||
}
|
||||
} else {
|
||||
if (!NAttrNonResident(na)) {
|
||||
ntfs_log_error("Cannot make non resident"
|
||||
" when a context has been allocated\n");
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
|
||||
/* no extra bytes are added to void attributes */
|
||||
oldsize = na->data_size;
|
||||
if (oldsize) {
|
||||
/* make sure size is valid for a raw encrypted stream */
|
||||
if ((oldsize & 511) != 2) {
|
||||
ntfs_log_error("Bad raw encrypted stream\n");
|
||||
goto err_out;
|
||||
}
|
||||
/* read padding length from last two bytes of attribute */
|
||||
if (ntfs_attr_pread(na, oldsize - 2, 2, &appended_bytes) != 2) {
|
||||
ntfs_log_error("Error reading padding length\n");
|
||||
goto err_out;
|
||||
}
|
||||
padding_length = le16_to_cpu(appended_bytes);
|
||||
if (padding_length > 511 || padding_length > na->data_size-2) {
|
||||
errno = EINVAL;
|
||||
ntfs_log_error("invalid padding length %d for data_size %lld\n",
|
||||
padding_length, (long long)oldsize);
|
||||
goto err_out;
|
||||
}
|
||||
newsize = oldsize - padding_length - 2;
|
||||
/*
|
||||
* truncate attribute to possibly free clusters allocated
|
||||
* for the last two bytes, but do not truncate to new size
|
||||
* to avoid losing useful data
|
||||
*/
|
||||
if (ntfs_attr_truncate(na, oldsize - 2)) {
|
||||
ntfs_log_error("Error truncating attribute\n");
|
||||
goto err_out;
|
||||
}
|
||||
} else
|
||||
newsize = 0;
|
||||
|
||||
/*
|
||||
* Encrypted AT_DATA Attributes MUST be non-resident
|
||||
* This has to be done after the attribute is resized, as
|
||||
* resizing down to zero may cause the attribute to be made
|
||||
* resident.
|
||||
*/
|
||||
if (!NAttrNonResident(na)
|
||||
&& ntfs_attr_make_non_resident(na, ctx)) {
|
||||
if (!close_ctx
|
||||
|| ntfs_attr_force_non_resident(na)) {
|
||||
ntfs_log_error("Error making DATA attribute non-resident\n");
|
||||
goto err_out;
|
||||
} else {
|
||||
/*
|
||||
* must reinitialize context after forcing
|
||||
* non-resident. We need a context for updating
|
||||
* the state, and at this point, we are sure
|
||||
* the context is not used elsewhere.
|
||||
*/
|
||||
ntfs_attr_reinit_search_ctx(ctx);
|
||||
if (ntfs_attr_lookup(AT_DATA, na->name, na->name_len,
|
||||
CASE_SENSITIVE, 0, NULL, 0, ctx)) {
|
||||
ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n");
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
ni = na->ni;
|
||||
if (!na->name_len) {
|
||||
ni->data_size = newsize;
|
||||
ni->allocated_size = na->allocated_size;
|
||||
}
|
||||
NInoSetDirty(ni);
|
||||
NInoFileNameSetDirty(ni);
|
||||
|
||||
ctx->attr->data_size = cpu_to_le64(newsize);
|
||||
if (le64_to_cpu(ctx->attr->initialized_size) > newsize)
|
||||
ctx->attr->initialized_size = ctx->attr->data_size;
|
||||
ctx->attr->flags |= ATTR_IS_ENCRYPTED;
|
||||
if (close_ctx)
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
|
||||
return (0);
|
||||
err_out:
|
||||
if (close_ctx && ctx)
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
#endif /* HAVE_SETXATTR */
|
30
source/efs.h
Normal file
30
source/efs.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2009 Martin Bene
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef EFS_H
|
||||
#define EFS_H
|
||||
|
||||
int ntfs_get_efs_info(ntfs_inode *ni, char *value, size_t size);
|
||||
|
||||
int ntfs_set_efs_info(ntfs_inode *ni,
|
||||
const char *value, size_t size, int flags);
|
||||
int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na);
|
||||
|
||||
#endif /* EFS_H */
|
203
source/endians.h
Normal file
203
source/endians.h
Normal file
@ -0,0 +1,203 @@
|
||||
/*
|
||||
* endians.h - Definitions related to handling of byte ordering.
|
||||
* Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2005 Anton Altaparmakov
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_ENDIANS_H
|
||||
#define _NTFS_ENDIANS_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Notes:
|
||||
* We define the conversion functions including typecasts since the
|
||||
* defaults don't necessarily perform appropriate typecasts.
|
||||
* Also, using our own functions means that we can change them if it
|
||||
* turns out that we do need to use the unaligned access macros on
|
||||
* architectures requiring aligned memory accesses...
|
||||
*/
|
||||
|
||||
#ifdef HAVE_ENDIAN_H
|
||||
#include <endian.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_ENDIAN_H
|
||||
#include <sys/endian.h>
|
||||
#endif
|
||||
#ifdef HAVE_MACHINE_ENDIAN_H
|
||||
#include <machine/endian.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_BYTEORDER_H
|
||||
#include <sys/byteorder.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_PARAM_H
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
|
||||
#ifndef __BYTE_ORDER
|
||||
# if defined(_BYTE_ORDER)
|
||||
# define __BYTE_ORDER _BYTE_ORDER
|
||||
# define __LITTLE_ENDIAN _LITTLE_ENDIAN
|
||||
# define __BIG_ENDIAN _BIG_ENDIAN
|
||||
# elif defined(BYTE_ORDER)
|
||||
# define __BYTE_ORDER BYTE_ORDER
|
||||
# define __LITTLE_ENDIAN LITTLE_ENDIAN
|
||||
# define __BIG_ENDIAN BIG_ENDIAN
|
||||
# elif defined(__BYTE_ORDER__)
|
||||
# define __BYTE_ORDER __BYTE_ORDER__
|
||||
# define __LITTLE_ENDIAN __LITTLE_ENDIAN__
|
||||
# define __BIG_ENDIAN __BIG_ENDIAN__
|
||||
# elif (defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)) || \
|
||||
defined(WORDS_LITTLEENDIAN)
|
||||
# define __BYTE_ORDER 1
|
||||
# define __LITTLE_ENDIAN 1
|
||||
# define __BIG_ENDIAN 0
|
||||
# elif (!defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN)) || \
|
||||
defined(WORDS_BIGENDIAN)
|
||||
# define __BYTE_ORDER 0
|
||||
# define __LITTLE_ENDIAN 1
|
||||
# define __BIG_ENDIAN 0
|
||||
# else
|
||||
# error "__BYTE_ORDER is not defined."
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define __ntfs_bswap_constant_16(x) \
|
||||
(u16)((((u16)(x) & 0xff00) >> 8) | \
|
||||
(((u16)(x) & 0x00ff) << 8))
|
||||
|
||||
#define __ntfs_bswap_constant_32(x) \
|
||||
(u32)((((u32)(x) & 0xff000000u) >> 24) | \
|
||||
(((u32)(x) & 0x00ff0000u) >> 8) | \
|
||||
(((u32)(x) & 0x0000ff00u) << 8) | \
|
||||
(((u32)(x) & 0x000000ffu) << 24))
|
||||
|
||||
#define __ntfs_bswap_constant_64(x) \
|
||||
(u64)((((u64)(x) & 0xff00000000000000ull) >> 56) | \
|
||||
(((u64)(x) & 0x00ff000000000000ull) >> 40) | \
|
||||
(((u64)(x) & 0x0000ff0000000000ull) >> 24) | \
|
||||
(((u64)(x) & 0x000000ff00000000ull) >> 8) | \
|
||||
(((u64)(x) & 0x00000000ff000000ull) << 8) | \
|
||||
(((u64)(x) & 0x0000000000ff0000ull) << 24) | \
|
||||
(((u64)(x) & 0x000000000000ff00ull) << 40) | \
|
||||
(((u64)(x) & 0x00000000000000ffull) << 56))
|
||||
|
||||
#ifdef HAVE_BYTESWAP_H
|
||||
# include <byteswap.h>
|
||||
#else
|
||||
# define bswap_16(x) __ntfs_bswap_constant_16(x)
|
||||
# define bswap_32(x) __ntfs_bswap_constant_32(x)
|
||||
# define bswap_64(x) __ntfs_bswap_constant_64(x)
|
||||
#endif
|
||||
|
||||
#if defined(__LITTLE_ENDIAN) && (__BYTE_ORDER == __LITTLE_ENDIAN)
|
||||
|
||||
#define __le16_to_cpu(x) (x)
|
||||
#define __le32_to_cpu(x) (x)
|
||||
#define __le64_to_cpu(x) (x)
|
||||
|
||||
#define __cpu_to_le16(x) (x)
|
||||
#define __cpu_to_le32(x) (x)
|
||||
#define __cpu_to_le64(x) (x)
|
||||
|
||||
#define __constant_le16_to_cpu(x) (x)
|
||||
#define __constant_le32_to_cpu(x) (x)
|
||||
#define __constant_le64_to_cpu(x) (x)
|
||||
|
||||
#define __constant_cpu_to_le16(x) (x)
|
||||
#define __constant_cpu_to_le32(x) (x)
|
||||
#define __constant_cpu_to_le64(x) (x)
|
||||
|
||||
#elif defined(__BIG_ENDIAN) && (__BYTE_ORDER == __BIG_ENDIAN)
|
||||
|
||||
#define __le16_to_cpu(x) bswap_16(x)
|
||||
#define __le32_to_cpu(x) bswap_32(x)
|
||||
#define __le64_to_cpu(x) bswap_64(x)
|
||||
|
||||
#define __cpu_to_le16(x) bswap_16(x)
|
||||
#define __cpu_to_le32(x) bswap_32(x)
|
||||
#define __cpu_to_le64(x) bswap_64(x)
|
||||
|
||||
#define __constant_le16_to_cpu(x) __ntfs_bswap_constant_16((u16)(x))
|
||||
#define __constant_le32_to_cpu(x) __ntfs_bswap_constant_32((u32)(x))
|
||||
#define __constant_le64_to_cpu(x) __ntfs_bswap_constant_64((u64)(x))
|
||||
|
||||
#define __constant_cpu_to_le16(x) __ntfs_bswap_constant_16((u16)(x))
|
||||
#define __constant_cpu_to_le32(x) __ntfs_bswap_constant_32((u32)(x))
|
||||
#define __constant_cpu_to_le64(x) __ntfs_bswap_constant_64((u64)(x))
|
||||
|
||||
#else
|
||||
|
||||
#error "You must define __BYTE_ORDER to be __LITTLE_ENDIAN or __BIG_ENDIAN."
|
||||
|
||||
#endif
|
||||
|
||||
/* Unsigned from LE to CPU conversion. */
|
||||
|
||||
#define le16_to_cpu(x) (u16)__le16_to_cpu((u16)(x))
|
||||
#define le32_to_cpu(x) (u32)__le32_to_cpu((u32)(x))
|
||||
#define le64_to_cpu(x) (u64)__le64_to_cpu((u64)(x))
|
||||
|
||||
#define le16_to_cpup(x) (u16)__le16_to_cpu(*(const u16*)(x))
|
||||
#define le32_to_cpup(x) (u32)__le32_to_cpu(*(const u32*)(x))
|
||||
#define le64_to_cpup(x) (u64)__le64_to_cpu(*(const u64*)(x))
|
||||
|
||||
/* Signed from LE to CPU conversion. */
|
||||
|
||||
#define sle16_to_cpu(x) (s16)__le16_to_cpu((s16)(x))
|
||||
#define sle32_to_cpu(x) (s32)__le32_to_cpu((s32)(x))
|
||||
#define sle64_to_cpu(x) (s64)__le64_to_cpu((s64)(x))
|
||||
|
||||
#define sle16_to_cpup(x) (s16)__le16_to_cpu(*(s16*)(x))
|
||||
#define sle32_to_cpup(x) (s32)__le32_to_cpu(*(s32*)(x))
|
||||
#define sle64_to_cpup(x) (s64)__le64_to_cpu(*(s64*)(x))
|
||||
|
||||
/* Unsigned from CPU to LE conversion. */
|
||||
|
||||
#define cpu_to_le16(x) (u16)__cpu_to_le16((u16)(x))
|
||||
#define cpu_to_le32(x) (u32)__cpu_to_le32((u32)(x))
|
||||
#define cpu_to_le64(x) (u64)__cpu_to_le64((u64)(x))
|
||||
|
||||
#define cpu_to_le16p(x) (u16)__cpu_to_le16(*(u16*)(x))
|
||||
#define cpu_to_le32p(x) (u32)__cpu_to_le32(*(u32*)(x))
|
||||
#define cpu_to_le64p(x) (u64)__cpu_to_le64(*(u64*)(x))
|
||||
|
||||
/* Signed from CPU to LE conversion. */
|
||||
|
||||
#define cpu_to_sle16(x) (s16)__cpu_to_le16((s16)(x))
|
||||
#define cpu_to_sle32(x) (s32)__cpu_to_le32((s32)(x))
|
||||
#define cpu_to_sle64(x) (s64)__cpu_to_le64((s64)(x))
|
||||
|
||||
#define cpu_to_sle16p(x) (s16)__cpu_to_le16(*(s16*)(x))
|
||||
#define cpu_to_sle32p(x) (s32)__cpu_to_le32(*(s32*)(x))
|
||||
#define cpu_to_sle64p(x) (s64)__cpu_to_le64(*(s64*)(x))
|
||||
|
||||
/* Constant endianness conversion defines. */
|
||||
|
||||
#define const_le16_to_cpu(x) __constant_le16_to_cpu(x)
|
||||
#define const_le32_to_cpu(x) __constant_le32_to_cpu(x)
|
||||
#define const_le64_to_cpu(x) __constant_le64_to_cpu(x)
|
||||
|
||||
#define const_cpu_to_le16(x) __constant_cpu_to_le16(x)
|
||||
#define const_cpu_to_le32(x) __constant_cpu_to_le32(x)
|
||||
#define const_cpu_to_le64(x) __constant_cpu_to_le64(x)
|
||||
|
||||
#endif /* defined _NTFS_ENDIANS_H */
|
647
source/gekko_io.c
Normal file
647
source/gekko_io.c
Normal file
@ -0,0 +1,647 @@
|
||||
/**
|
||||
* gekko_io.c - Gekko style disk io functions.
|
||||
*
|
||||
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
||||
* Copyright (c) 2010 Dimok
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef HAVE_MATH_H
|
||||
#include <math.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_LIMITS_H
|
||||
#include <limits.h>
|
||||
#endif
|
||||
#ifdef HAVE_LOCALE_H
|
||||
#include <locale.h>
|
||||
#endif
|
||||
|
||||
#include "ntfs.h"
|
||||
#include "types.h"
|
||||
#include "logging.h"
|
||||
#include "device_io.h"
|
||||
#include "gekko_io.h"
|
||||
#include "cache.h"
|
||||
#include "device.h"
|
||||
#include "bootsect.h"
|
||||
|
||||
#define DEV_FD(dev) ((gekko_fd *)dev->d_private)
|
||||
|
||||
/* Prototypes */
|
||||
static s64 ntfs_device_gekko_io_readbytes(struct ntfs_device *dev, s64 offset, s64 count, void *buf);
|
||||
static bool ntfs_device_gekko_io_readsectors(struct ntfs_device *dev, sec_t sector, sec_t numSectors, void* buffer);
|
||||
static s64 ntfs_device_gekko_io_writebytes(struct ntfs_device *dev, s64 offset, s64 count, const void *buf);
|
||||
static bool ntfs_device_gekko_io_writesectors(struct ntfs_device *dev, sec_t sector, sec_t numSectors, const void* buffer);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int ntfs_device_gekko_io_open(struct ntfs_device *dev, int flags)
|
||||
{
|
||||
ntfs_log_trace("dev %p, flags %i\n", dev, flags);
|
||||
|
||||
// Get the device driver descriptor
|
||||
gekko_fd *fd = DEV_FD(dev);
|
||||
if (!fd) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get the device interface
|
||||
const DISC_INTERFACE* interface = fd->interface;
|
||||
if (!interface) {
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Start the device interface and ensure that it is inserted
|
||||
if (!interface->startup()) {
|
||||
ntfs_log_perror("device failed to start\n");
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
if (!interface->isInserted()) {
|
||||
ntfs_log_perror("device media is not inserted\n");
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check that the device isn't already open (used by another volume?)
|
||||
if (NDevOpen(dev)) {
|
||||
ntfs_log_perror("device is busy (already open)\n");
|
||||
errno = EBUSY;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check that there is a valid NTFS boot sector at the start of the device
|
||||
NTFS_BOOT_SECTOR boot;
|
||||
if (interface->readSectors(fd->startSector, 1, &boot)) {
|
||||
if (!ntfs_boot_sector_is_ntfs(&boot)) {
|
||||
errno = EINVALPART;
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
ntfs_log_perror("read failure @ sector %d\n", fd->startSector);
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Parse the boot sector
|
||||
fd->hiddenSectors = le32_to_cpu(boot.bpb.hidden_sectors);
|
||||
fd->sectorSize = le16_to_cpu(boot.bpb.bytes_per_sector);
|
||||
fd->sectorCount = sle64_to_cpu(boot.number_of_sectors);
|
||||
fd->pos = 0;
|
||||
fd->len = (fd->sectorCount * fd->sectorSize);
|
||||
fd->ino = le64_to_cpu(boot.volume_serial_number);
|
||||
|
||||
// Mark the device as read-only (if required)
|
||||
if (flags & O_RDONLY) {
|
||||
NDevSetReadOnly(dev);
|
||||
}
|
||||
|
||||
// Create the cache
|
||||
fd->cache = _NTFS_cache_constructor(fd->cachePageCount, fd->cachePageSize, interface, fd->startSector + fd->sectorCount, fd->sectorSize);
|
||||
|
||||
// Mark the device as open
|
||||
NDevSetBlock(dev);
|
||||
NDevSetOpen(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int ntfs_device_gekko_io_close(struct ntfs_device *dev)
|
||||
{
|
||||
ntfs_log_trace("dev %p\n", dev);
|
||||
|
||||
// Get the device driver descriptor
|
||||
gekko_fd *fd = DEV_FD(dev);
|
||||
if (!fd) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check that the device is actually open
|
||||
if (!NDevOpen(dev)) {
|
||||
ntfs_log_perror("device is not open\n");
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Mark the device as closed
|
||||
NDevClearOpen(dev);
|
||||
NDevClearBlock(dev);
|
||||
|
||||
// Flush the device (if dirty and not read-only)
|
||||
if (NDevDirty(dev) && !NDevReadOnly(dev)) {
|
||||
ntfs_log_debug("device is dirty, will now sync\n");
|
||||
|
||||
// ...?
|
||||
|
||||
// Mark the device as clean
|
||||
NDevClearDirty(dev);
|
||||
|
||||
}
|
||||
|
||||
// Flush and destroy the cache (if required)
|
||||
if (fd->cache) {
|
||||
_NTFS_cache_flush(fd->cache);
|
||||
_NTFS_cache_destructor(fd->cache);
|
||||
}
|
||||
|
||||
// Shutdown the device interface
|
||||
/*const DISC_INTERFACE* interface = fd->interface;
|
||||
if (interface) {
|
||||
interface->shutdown();
|
||||
}*/
|
||||
|
||||
// Free the device driver private data
|
||||
ntfs_free(dev->d_private);
|
||||
dev->d_private = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static s64 ntfs_device_gekko_io_seek(struct ntfs_device *dev, s64 offset, int whence)
|
||||
{
|
||||
ntfs_log_trace("dev %p, offset %Li, whence %i\n", dev, offset, whence);
|
||||
|
||||
// Get the device driver descriptor
|
||||
gekko_fd *fd = DEV_FD(dev);
|
||||
if (!fd) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Set the current position on the device (in bytes)
|
||||
switch(whence) {
|
||||
case SEEK_SET: fd->pos = MIN(MAX(offset, 0), fd->len); break;
|
||||
case SEEK_CUR: fd->pos = MIN(MAX(fd->pos + offset, 0), fd->len); break;
|
||||
case SEEK_END: fd->pos = MIN(MAX(fd->len + offset, 0), fd->len); break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static s64 ntfs_device_gekko_io_read(struct ntfs_device *dev, void *buf, s64 count)
|
||||
{
|
||||
return ntfs_device_gekko_io_readbytes(dev, DEV_FD(dev)->pos, count, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static s64 ntfs_device_gekko_io_write(struct ntfs_device *dev, const void *buf, s64 count)
|
||||
{
|
||||
return ntfs_device_gekko_io_writebytes(dev, DEV_FD(dev)->pos, count, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static s64 ntfs_device_gekko_io_pread(struct ntfs_device *dev, void *buf, s64 count, s64 offset)
|
||||
{
|
||||
return ntfs_device_gekko_io_readbytes(dev, offset, count, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static s64 ntfs_device_gekko_io_pwrite(struct ntfs_device *dev, const void *buf, s64 count, s64 offset)
|
||||
{
|
||||
return ntfs_device_gekko_io_writebytes(dev, offset, count, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static s64 ntfs_device_gekko_io_readbytes(struct ntfs_device *dev, s64 offset, s64 count, void *buf)
|
||||
{
|
||||
ntfs_log_trace("dev %p, offset %Li, count %Li\n", dev, offset, count);
|
||||
|
||||
// Get the device driver descriptor
|
||||
gekko_fd *fd = DEV_FD(dev);
|
||||
if (!fd) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get the device interface
|
||||
const DISC_INTERFACE* interface = fd->interface;
|
||||
if (!interface) {
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(offset < 0)
|
||||
{
|
||||
errno = EROFS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!count)
|
||||
return 0;
|
||||
|
||||
sec_t sec_start = (sec_t) fd->startSector;
|
||||
sec_t sec_count = 1;
|
||||
u32 buffer_offset = (u32) (offset % fd->sectorSize);
|
||||
u8 *buffer = NULL;
|
||||
|
||||
// Determine the range of sectors required for this read
|
||||
if (offset > 0) {
|
||||
sec_start += (sec_t) floor((f64) offset / (f64) fd->sectorSize);
|
||||
}
|
||||
if (buffer_offset+count > fd->sectorSize) {
|
||||
sec_count = (sec_t) ceil((f64) (buffer_offset+count) / (f64) fd->sectorSize);
|
||||
}
|
||||
|
||||
// If this read happens to be on the sector boundaries then do the read straight into the destination buffer
|
||||
|
||||
if((buffer_offset == 0) && (count % fd->sectorSize == 0)) {
|
||||
|
||||
// Read from the device
|
||||
ntfs_log_trace("direct read from sector %d (%d sector(s) long)\n", sec_start, sec_count);
|
||||
if (!ntfs_device_gekko_io_readsectors(dev, sec_start, sec_count, buf)) {
|
||||
ntfs_log_perror("direct read failure @ sector %d (%d sector(s) long)\n", sec_start, sec_count);
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Else read into a buffer and copy over only what was requested
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// Allocate a buffer to hold the read data
|
||||
buffer = (u8*)ntfs_alloc(sec_count * fd->sectorSize);
|
||||
if (!buffer) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Read from the device
|
||||
ntfs_log_trace("buffered read from sector %d (%d sector(s) long)\n", sec_start, sec_count);
|
||||
ntfs_log_trace("count: %d sec_count:%d fd->sectorSize: %d )\n", (u32)count, (u32)sec_count,(u32)fd->sectorSize);
|
||||
if (!ntfs_device_gekko_io_readsectors(dev, sec_start, sec_count, buffer)) {
|
||||
ntfs_log_perror("buffered read failure @ sector %d (%d sector(s) long)\n", sec_start, sec_count);
|
||||
ntfs_free(buffer);
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Copy what was requested to the destination buffer
|
||||
memcpy(buf, buffer + buffer_offset, count);
|
||||
ntfs_free(buffer);
|
||||
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static s64 ntfs_device_gekko_io_writebytes(struct ntfs_device *dev, s64 offset, s64 count, const void *buf)
|
||||
{
|
||||
ntfs_log_trace("dev %p, offset %lli, count %lli\n", dev, offset, count);
|
||||
|
||||
// Get the device driver descriptor
|
||||
gekko_fd *fd = DEV_FD(dev);
|
||||
if (!fd) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get the device interface
|
||||
const DISC_INTERFACE* interface = fd->interface;
|
||||
if (!interface) {
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check that the device can be written to
|
||||
if (NDevReadOnly(dev)) {
|
||||
errno = EROFS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(count < 0 || offset < 0) {
|
||||
errno = EROFS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(count == 0)
|
||||
return 0;
|
||||
|
||||
sec_t sec_start = (sec_t) fd->startSector;
|
||||
sec_t sec_count = 1;
|
||||
u32 buffer_offset = (u32) (offset % fd->sectorSize);
|
||||
u8 *buffer = NULL;
|
||||
|
||||
// Determine the range of sectors required for this write
|
||||
if (offset > 0) {
|
||||
sec_start += (sec_t) floor((f64) offset / (f64) fd->sectorSize);
|
||||
}
|
||||
if ((buffer_offset+count) > fd->sectorSize) {
|
||||
sec_count = (sec_t) ceil((f64) (buffer_offset+count) / (f64) fd->sectorSize);
|
||||
}
|
||||
|
||||
// If this write happens to be on the sector boundaries then do the write straight to disc
|
||||
if((buffer_offset == 0) && (count % fd->sectorSize == 0))
|
||||
{
|
||||
// Write to the device
|
||||
ntfs_log_trace("direct write to sector %d (%d sector(s) long)\n", sec_start, sec_count);
|
||||
if (!ntfs_device_gekko_io_writesectors(dev, sec_start, sec_count, buf)) {
|
||||
ntfs_log_perror("direct write failure @ sector %d (%d sector(s) long)\n", sec_start, sec_count);
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
// Else write from a buffer aligned to the sector boundaries
|
||||
}
|
||||
else
|
||||
{
|
||||
// Allocate a buffer to hold the write data
|
||||
buffer = (u8 *) ntfs_alloc(sec_count * fd->sectorSize);
|
||||
if (!buffer) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
// Read the first and last sectors of the buffer from disc (if required)
|
||||
// NOTE: This is done because the data does not line up with the sector boundaries,
|
||||
// we just read in the buffer edges where the data overlaps with the rest of the disc
|
||||
if(buffer_offset != 0)
|
||||
{
|
||||
if (!ntfs_device_gekko_io_readsectors(dev, sec_start, 1, buffer)) {
|
||||
ntfs_log_perror("read failure @ sector %d\n", sec_start);
|
||||
ntfs_free(buffer);
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if((buffer_offset+count) % fd->sectorSize != 0)
|
||||
{
|
||||
if (!ntfs_device_gekko_io_readsectors(dev, sec_start + sec_count - 1, 1, buffer + ((sec_count-1) * fd->sectorSize))) {
|
||||
ntfs_log_perror("read failure @ sector %d\n", sec_start + sec_count - 1);
|
||||
ntfs_free(buffer);
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the data into the write buffer
|
||||
memcpy(buffer + buffer_offset, buf, count);
|
||||
|
||||
// Write to the device
|
||||
ntfs_log_trace("buffered write to sector %d (%d sector(s) long)\n", sec_start, sec_count);
|
||||
if (!ntfs_device_gekko_io_writesectors(dev, sec_start, sec_count, buffer)) {
|
||||
ntfs_log_perror("buffered write failure @ sector %d\n", sec_start);
|
||||
ntfs_free(buffer);
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Free the buffer
|
||||
ntfs_free(buffer);
|
||||
}
|
||||
|
||||
// Mark the device as dirty (if we actually wrote anything)
|
||||
NDevSetDirty(dev);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static bool ntfs_device_gekko_io_readsectors(struct ntfs_device *dev, sec_t sector, sec_t numSectors, void* buffer)
|
||||
{
|
||||
// Get the device driver descriptor
|
||||
gekko_fd *fd = DEV_FD(dev);
|
||||
if (!fd) {
|
||||
errno = EBADF;
|
||||
return false;
|
||||
}
|
||||
// Read the sectors from disc (or cache, if enabled)
|
||||
if (fd->cache)
|
||||
return _NTFS_cache_readSectors(fd->cache, sector, numSectors, buffer);
|
||||
else
|
||||
return fd->interface->readSectors(sector, numSectors, buffer);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ntfs_device_gekko_io_writesectors(struct ntfs_device *dev, sec_t sector, sec_t numSectors, const void* buffer)
|
||||
{
|
||||
// Get the device driver descriptor
|
||||
gekko_fd *fd = DEV_FD(dev);
|
||||
if (!fd) {
|
||||
errno = EBADF;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write the sectors to disc (or cache, if enabled)
|
||||
if (fd->cache)
|
||||
return _NTFS_cache_writeSectors(fd->cache, sector, numSectors, buffer);
|
||||
else
|
||||
return fd->interface->writeSectors(sector, numSectors, buffer);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int ntfs_device_gekko_io_sync(struct ntfs_device *dev)
|
||||
{
|
||||
gekko_fd *fd = DEV_FD(dev);
|
||||
ntfs_log_trace("dev %p\n", dev);
|
||||
|
||||
// Check that the device can be written to
|
||||
if (NDevReadOnly(dev)) {
|
||||
errno = EROFS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Mark the device as clean
|
||||
NDevClearDirty(dev);
|
||||
|
||||
// Flush any sectors in the disc cache (if required)
|
||||
if (fd->cache) {
|
||||
if (!_NTFS_cache_flush(fd->cache)) {
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int ntfs_device_gekko_io_stat(struct ntfs_device *dev, struct stat *buf)
|
||||
{
|
||||
ntfs_log_trace("dev %p, buf %p\n", dev, buf);
|
||||
|
||||
// Get the device driver descriptor
|
||||
gekko_fd *fd = DEV_FD(dev);
|
||||
if (!fd) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Short circuit cases were we don't actually have to do anything
|
||||
if (!buf)
|
||||
return 0;
|
||||
|
||||
// Build the device mode
|
||||
mode_t mode = (S_IFBLK) |
|
||||
(S_IRUSR | S_IRGRP | S_IROTH) |
|
||||
((!NDevReadOnly(dev)) ? (S_IWUSR | S_IWGRP | S_IWOTH) : 0);
|
||||
|
||||
// Zero out the stat buffer
|
||||
memset(buf, 0, sizeof(struct stat));
|
||||
|
||||
// Build the device stats
|
||||
buf->st_dev = fd->interface->ioType;
|
||||
buf->st_ino = fd->ino;
|
||||
buf->st_mode = mode;
|
||||
buf->st_rdev = fd->interface->ioType;
|
||||
buf->st_blksize = fd->sectorSize;
|
||||
buf->st_blocks = fd->sectorCount;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int ntfs_device_gekko_io_ioctl(struct ntfs_device *dev, int request, void *argp)
|
||||
{
|
||||
ntfs_log_trace("dev %p, request %i, argp %p\n", dev, request, argp);
|
||||
|
||||
// Get the device driver descriptor
|
||||
gekko_fd *fd = DEV_FD(dev);
|
||||
if (!fd) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Figure out which i/o control was requested
|
||||
switch (request) {
|
||||
|
||||
// Get block device size (sectors)
|
||||
#if defined(BLKGETSIZE)
|
||||
case BLKGETSIZE: {
|
||||
*(u32*)argp = fd->sectorCount;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Get block device size (bytes)
|
||||
#if defined(BLKGETSIZE64)
|
||||
case BLKGETSIZE64: {
|
||||
*(u64*)argp = (fd->sectorCount * fd->sectorSize);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Get hard drive geometry
|
||||
#if defined(HDIO_GETGEO)
|
||||
case HDIO_GETGEO: {
|
||||
struct hd_geometry *geo = (struct hd_geometry*)argp;
|
||||
geo->sectors = 0;
|
||||
geo->heads = 0;
|
||||
geo->cylinders = 0;
|
||||
geo->start = fd->hiddenSectors;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Get block device sector size (bytes)
|
||||
#if defined(BLKSSZGET)
|
||||
case BLKSSZGET: {
|
||||
*(int*)argp = fd->sectorSize;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Set block device block size (bytes)
|
||||
#if defined(BLKBSZSET)
|
||||
case BLKBSZSET: {
|
||||
int sectorSize = *(int*)argp;
|
||||
fd->sectorSize = sectorSize;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Unimplemented ioctrl
|
||||
default: {
|
||||
ntfs_log_perror("Unimplemented ioctrl %i\n", request);
|
||||
errno = EOPNOTSUPP;
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Device operations for working with gekko style devices and files.
|
||||
*/
|
||||
struct ntfs_device_operations ntfs_device_gekko_io_ops = {
|
||||
.open = ntfs_device_gekko_io_open,
|
||||
.close = ntfs_device_gekko_io_close,
|
||||
.seek = ntfs_device_gekko_io_seek,
|
||||
.read = ntfs_device_gekko_io_read,
|
||||
.write = ntfs_device_gekko_io_write,
|
||||
.pread = ntfs_device_gekko_io_pread,
|
||||
.pwrite = ntfs_device_gekko_io_pwrite,
|
||||
.sync = ntfs_device_gekko_io_sync,
|
||||
.stat = ntfs_device_gekko_io_stat,
|
||||
.ioctl = ntfs_device_gekko_io_ioctl,
|
||||
};
|
56
source/gekko_io.h
Normal file
56
source/gekko_io.h
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* gekko_io.h - Platform specifics for device io.
|
||||
*
|
||||
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _GEKKO_IO_H
|
||||
#define _GEKKO_IO_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "cache2.h"
|
||||
#include <gccore.h>
|
||||
#include <ogc/disc_io.h>
|
||||
|
||||
/**
|
||||
* gekko_fd - Gekko device driver descriptor
|
||||
*/
|
||||
typedef struct _gekko_fd {
|
||||
const DISC_INTERFACE* interface; /* Device disc interface */
|
||||
sec_t startSector; /* LBA of partition start */
|
||||
sec_t hiddenSectors; /* LBA offset to true partition start (as described by boot sector) */
|
||||
u16 sectorSize; /* Device sector size (in bytes) */
|
||||
u64 sectorCount; /* Total number of sectors in partition */
|
||||
u64 pos; /* Current position within the partition (in bytes) */
|
||||
u64 len; /* Total length of partition (in bytes) */
|
||||
ino_t ino; /* Device identifier */
|
||||
NTFS_CACHE *cache; /* Cache */
|
||||
u32 cachePageCount; /* The number of pages in the cache */
|
||||
u32 cachePageSize; /* The number of sectors per cache page */
|
||||
} gekko_fd;
|
||||
|
||||
/* Forward declarations */
|
||||
struct ntfs_device_operations;
|
||||
|
||||
/* Gekko device driver i/o operations */
|
||||
extern struct ntfs_device_operations ntfs_device_gekko_io_ops;
|
||||
|
||||
#endif /* _GEKKO_IO_H */
|
2063
source/index.c
Normal file
2063
source/index.c
Normal file
File diff suppressed because it is too large
Load Diff
167
source/index.h
Normal file
167
source/index.h
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* index.h - Defines for NTFS index handling. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2004 Anton Altaparmakov
|
||||
* Copyright (c) 2004-2005 Richard Russon
|
||||
* Copyright (c) 2005 Yura Pakhuchiy
|
||||
* Copyright (c) 2006-2008 Szabolcs Szakacsits
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_INDEX_H
|
||||
#define _NTFS_INDEX_H
|
||||
|
||||
/* Convenience macros to test the versions of gcc.
|
||||
* Use them like this:
|
||||
* #if __GNUC_PREREQ (2,8)
|
||||
* ... code requiring gcc 2.8 or later ...
|
||||
* #endif
|
||||
* Note - they won't work for gcc1 or glibc1, since the _MINOR macros
|
||||
* were not defined then.
|
||||
*/
|
||||
|
||||
#ifndef __GNUC_PREREQ
|
||||
# if defined __GNUC__ && defined __GNUC_MINOR__
|
||||
# define __GNUC_PREREQ(maj, min) \
|
||||
((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
|
||||
# else
|
||||
# define __GNUC_PREREQ(maj, min) 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* allows us to warn about unused results of certain function calls */
|
||||
#ifndef __attribute_warn_unused_result__
|
||||
# if __GNUC_PREREQ (3,4)
|
||||
# define __attribute_warn_unused_result__ \
|
||||
__attribute__ ((__warn_unused_result__))
|
||||
# else
|
||||
# define __attribute_warn_unused_result__ /* empty */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include "attrib.h"
|
||||
#include "types.h"
|
||||
#include "layout.h"
|
||||
#include "inode.h"
|
||||
#include "mft.h"
|
||||
|
||||
#define VCN_INDEX_ROOT_PARENT ((VCN)-2)
|
||||
|
||||
#define MAX_PARENT_VCN 32
|
||||
|
||||
typedef int (*COLLATE)(ntfs_volume *vol, const void *data1, int len1,
|
||||
const void *data2, int len2);
|
||||
|
||||
/**
|
||||
* struct ntfs_index_context -
|
||||
* @ni: inode containing the @entry described by this context
|
||||
* @name: name of the index described by this context
|
||||
* @name_len: length of the index name
|
||||
* @entry: index entry (points into @ir or @ia)
|
||||
* @data: index entry data (points into @entry)
|
||||
* @data_len: length in bytes of @data
|
||||
* @is_in_root: TRUE if @entry is in @ir or FALSE if it is in @ia
|
||||
* @ir: index root if @is_in_root or NULL otherwise
|
||||
* @actx: attribute search context if in root or NULL otherwise
|
||||
* @ia: index block if @is_in_root is FALSE or NULL otherwise
|
||||
* @ia_na: opened INDEX_ALLOCATION attribute
|
||||
* @parent_pos: parent entries' positions in the index block
|
||||
* @parent_vcn: entry's parent node or VCN_INDEX_ROOT_PARENT
|
||||
* @new_vcn: new VCN if we need to create a new index block
|
||||
* @median: move to the parent if splitting index blocks
|
||||
* @ib_dirty: TRUE if index block was changed
|
||||
* @block_size: index block size
|
||||
* @vcn_size_bits: VCN size bits for this index block
|
||||
*
|
||||
* @ni is the inode this context belongs to.
|
||||
*
|
||||
* @entry is the index entry described by this context. @data and @data_len
|
||||
* are the index entry data and its length in bytes, respectively. @data
|
||||
* simply points into @entry. This is probably what the user is interested in.
|
||||
*
|
||||
* If @is_in_root is TRUE, @entry is in the index root attribute @ir described
|
||||
* by the attribute search context @actx and inode @ni. @ia and
|
||||
* @ib_dirty are undefined in this case.
|
||||
*
|
||||
* If @is_in_root is FALSE, @entry is in the index allocation attribute and @ia
|
||||
* point to the index allocation block and VCN where it's placed,
|
||||
* respectively. @ir and @actx are NULL in this case. @ia_na is opened
|
||||
* INDEX_ALLOCATION attribute. @ib_dirty is TRUE if index block was changed and
|
||||
* FALSE otherwise.
|
||||
*
|
||||
* To obtain a context call ntfs_index_ctx_get().
|
||||
*
|
||||
* When finished with the @entry and its @data, call ntfs_index_ctx_put() to
|
||||
* free the context and other associated resources.
|
||||
*
|
||||
* If the index entry was modified, call ntfs_index_entry_mark_dirty() before
|
||||
* the call to ntfs_index_ctx_put() to ensure that the changes are written
|
||||
* to disk.
|
||||
*/
|
||||
typedef struct {
|
||||
ntfs_inode *ni;
|
||||
ntfschar *name;
|
||||
u32 name_len;
|
||||
INDEX_ENTRY *entry;
|
||||
void *data;
|
||||
u16 data_len;
|
||||
COLLATE collate;
|
||||
BOOL is_in_root;
|
||||
INDEX_ROOT *ir;
|
||||
ntfs_attr_search_ctx *actx;
|
||||
INDEX_BLOCK *ib;
|
||||
ntfs_attr *ia_na;
|
||||
int parent_pos[MAX_PARENT_VCN]; /* parent entries' positions */
|
||||
VCN parent_vcn[MAX_PARENT_VCN]; /* entry's parent nodes */
|
||||
int pindex; /* maximum it's the number of the parent nodes */
|
||||
BOOL ib_dirty;
|
||||
u32 block_size;
|
||||
u8 vcn_size_bits;
|
||||
} ntfs_index_context;
|
||||
|
||||
extern ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *ni,
|
||||
ntfschar *name, u32 name_len);
|
||||
extern void ntfs_index_ctx_put(ntfs_index_context *ictx);
|
||||
extern void ntfs_index_ctx_reinit(ntfs_index_context *ictx);
|
||||
|
||||
extern int ntfs_index_lookup(const void *key, const int key_len,
|
||||
ntfs_index_context *ictx) __attribute_warn_unused_result__;
|
||||
|
||||
extern INDEX_ENTRY *ntfs_index_next(INDEX_ENTRY *ie,
|
||||
ntfs_index_context *ictx);
|
||||
|
||||
extern int ntfs_index_add_filename(ntfs_inode *ni, FILE_NAME_ATTR *fn,
|
||||
MFT_REF mref);
|
||||
extern int ntfs_index_remove(ntfs_inode *dir_ni, ntfs_inode *ni,
|
||||
const void *key, const int keylen);
|
||||
|
||||
extern INDEX_ROOT *ntfs_index_root_get(ntfs_inode *ni, ATTR_RECORD *attr);
|
||||
|
||||
extern VCN ntfs_ie_get_vcn(INDEX_ENTRY *ie);
|
||||
|
||||
extern void ntfs_index_entry_mark_dirty(ntfs_index_context *ictx);
|
||||
|
||||
extern char *ntfs_ie_filename_get(INDEX_ENTRY *ie);
|
||||
extern void ntfs_ie_filename_dump(INDEX_ENTRY *ie);
|
||||
extern void ntfs_ih_filename_dump(INDEX_HEADER *ih);
|
||||
|
||||
/* the following was added by JPA for use in security.c */
|
||||
extern int ntfs_ie_add(ntfs_index_context *icx, INDEX_ENTRY *ie);
|
||||
extern int ntfs_index_rm(ntfs_index_context *icx);
|
||||
|
||||
#endif /* _NTFS_INDEX_H */
|
||||
|
1566
source/inode.c
Normal file
1566
source/inode.c
Normal file
File diff suppressed because it is too large
Load Diff
225
source/inode.h
Normal file
225
source/inode.h
Normal file
@ -0,0 +1,225 @@
|
||||
/*
|
||||
* inode.h - Defines for NTFS inode handling. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2001-2004 Anton Altaparmakov
|
||||
* Copyright (c) 2004-2007 Yura Pakhuchiy
|
||||
* Copyright (c) 2004-2005 Richard Russon
|
||||
* Copyright (c) 2006-2008 Szabolcs Szakacsits
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_INODE_H
|
||||
#define _NTFS_INODE_H
|
||||
|
||||
/* Forward declaration */
|
||||
typedef struct _ntfs_inode ntfs_inode;
|
||||
|
||||
#include "types.h"
|
||||
#include "layout.h"
|
||||
#include "support.h"
|
||||
#include "volume.h"
|
||||
#include "ntfstime.h"
|
||||
|
||||
/**
|
||||
* enum ntfs_inode_state_bits -
|
||||
*
|
||||
* Defined bits for the state field in the ntfs_inode structure.
|
||||
* (f) = files only, (d) = directories only
|
||||
*/
|
||||
typedef enum {
|
||||
NI_Dirty, /* 1: Mft record needs to be written to disk. */
|
||||
|
||||
/* The NI_AttrList* tests only make sense for base inodes. */
|
||||
NI_AttrList, /* 1: Mft record contains an attribute list. */
|
||||
NI_AttrListDirty, /* 1: Attribute list needs to be written to the
|
||||
mft record and then to disk. */
|
||||
NI_FileNameDirty, /* 1: FILE_NAME attributes need to be updated
|
||||
in the index. */
|
||||
NI_v3_Extensions, /* 1: JPA v3.x extensions present. */
|
||||
NI_TimesSet, /* 1: Use times which were set */
|
||||
NI_KnownSize, /* 1: Set if sizes are meaningful */
|
||||
} ntfs_inode_state_bits;
|
||||
|
||||
#define test_nino_flag(ni, flag) test_bit(NI_##flag, (ni)->state)
|
||||
#define set_nino_flag(ni, flag) set_bit(NI_##flag, (ni)->state)
|
||||
#define clear_nino_flag(ni, flag) clear_bit(NI_##flag, (ni)->state)
|
||||
|
||||
#define test_and_set_nino_flag(ni, flag) \
|
||||
test_and_set_bit(NI_##flag, (ni)->state)
|
||||
#define test_and_clear_nino_flag(ni, flag) \
|
||||
test_and_clear_bit(NI_##flag, (ni)->state)
|
||||
|
||||
#define NInoDirty(ni) test_nino_flag(ni, Dirty)
|
||||
#define NInoSetDirty(ni) set_nino_flag(ni, Dirty)
|
||||
#define NInoClearDirty(ni) clear_nino_flag(ni, Dirty)
|
||||
#define NInoTestAndSetDirty(ni) test_and_set_nino_flag(ni, Dirty)
|
||||
#define NInoTestAndClearDirty(ni) test_and_clear_nino_flag(ni, Dirty)
|
||||
|
||||
#define NInoAttrList(ni) test_nino_flag(ni, AttrList)
|
||||
#define NInoSetAttrList(ni) set_nino_flag(ni, AttrList)
|
||||
#define NInoClearAttrList(ni) clear_nino_flag(ni, AttrList)
|
||||
|
||||
|
||||
#define test_nino_al_flag(ni, flag) test_nino_flag(ni, AttrList##flag)
|
||||
#define set_nino_al_flag(ni, flag) set_nino_flag(ni, AttrList##flag)
|
||||
#define clear_nino_al_flag(ni, flag) clear_nino_flag(ni, AttrList##flag)
|
||||
|
||||
#define test_and_set_nino_al_flag(ni, flag) \
|
||||
test_and_set_nino_flag(ni, AttrList##flag)
|
||||
#define test_and_clear_nino_al_flag(ni, flag) \
|
||||
test_and_clear_nino_flag(ni, AttrList##flag)
|
||||
|
||||
#define NInoAttrListDirty(ni) test_nino_al_flag(ni, Dirty)
|
||||
#define NInoAttrListSetDirty(ni) set_nino_al_flag(ni, Dirty)
|
||||
#define NInoAttrListClearDirty(ni) clear_nino_al_flag(ni, Dirty)
|
||||
#define NInoAttrListTestAndSetDirty(ni) test_and_set_nino_al_flag(ni, Dirty)
|
||||
#define NInoAttrListTestAndClearDirty(ni) test_and_clear_nino_al_flag(ni, Dirty)
|
||||
|
||||
#define NInoFileNameDirty(ni) test_nino_flag(ni, FileNameDirty)
|
||||
#define NInoFileNameSetDirty(ni) set_nino_flag(ni, FileNameDirty)
|
||||
#define NInoFileNameClearDirty(ni) clear_nino_flag(ni, FileNameDirty)
|
||||
#define NInoFileNameTestAndSetDirty(ni) \
|
||||
test_and_set_nino_flag(ni, FileNameDirty)
|
||||
#define NInoFileNameTestAndClearDirty(ni) \
|
||||
test_and_clear_nino_flag(ni, FileNameDirty)
|
||||
|
||||
/**
|
||||
* struct _ntfs_inode - The NTFS in-memory inode structure.
|
||||
*
|
||||
* It is just used as an extension to the fields already provided in the VFS
|
||||
* inode.
|
||||
*/
|
||||
struct _ntfs_inode {
|
||||
u64 mft_no; /* Inode / mft record number. */
|
||||
MFT_RECORD *mrec; /* The actual mft record of the inode. */
|
||||
ntfs_volume *vol; /* Pointer to the ntfs volume of this inode. */
|
||||
unsigned long state; /* NTFS specific flags describing this inode.
|
||||
See ntfs_inode_state_bits above. */
|
||||
FILE_ATTR_FLAGS flags; /* Flags describing the file.
|
||||
(Copy from STANDARD_INFORMATION) */
|
||||
/*
|
||||
* Attribute list support (for use by the attribute lookup functions).
|
||||
* Setup during ntfs_open_inode() for all inodes with attribute lists.
|
||||
* Only valid if NI_AttrList is set in state.
|
||||
*/
|
||||
u32 attr_list_size; /* Length of attribute list value in bytes. */
|
||||
u8 *attr_list; /* Attribute list value itself. */
|
||||
/* Below fields are always valid. */
|
||||
s32 nr_extents; /* For a base mft record, the number of
|
||||
attached extent inodes (0 if none), for
|
||||
extent records this is -1. */
|
||||
union { /* This union is only used if nr_extents != 0. */
|
||||
ntfs_inode **extent_nis;/* For nr_extents > 0, array of the
|
||||
ntfs inodes of the extent mft
|
||||
records belonging to this base
|
||||
inode which have been loaded. */
|
||||
ntfs_inode *base_ni; /* For nr_extents == -1, the ntfs
|
||||
inode of the base mft record. */
|
||||
};
|
||||
|
||||
/* Below fields are valid only for base inode. */
|
||||
|
||||
/*
|
||||
* These two fields are used to sync filename index and guaranteed to be
|
||||
* correct, however value in index itself maybe wrong (windows itself
|
||||
* do not update them properly).
|
||||
* For directories, they hold the index size, provided the
|
||||
* flag KnownSize is set.
|
||||
*/
|
||||
s64 data_size; /* Data size of unnamed DATA attribute
|
||||
(or INDEX_ROOT for directories) */
|
||||
s64 allocated_size; /* Allocated size stored in the filename
|
||||
index. (NOTE: Equal to allocated size of
|
||||
the unnamed data attribute for normal or
|
||||
encrypted files and to compressed size
|
||||
of the unnamed data attribute for sparse or
|
||||
compressed files.) */
|
||||
|
||||
/*
|
||||
* These four fields are copy of relevant fields from
|
||||
* STANDARD_INFORMATION attribute and used to sync it and FILE_NAME
|
||||
* attribute in the index.
|
||||
*/
|
||||
ntfs_time creation_time;
|
||||
ntfs_time last_data_change_time;
|
||||
ntfs_time last_mft_change_time;
|
||||
ntfs_time last_access_time;
|
||||
/* NTFS 3.x extensions added by JPA */
|
||||
/* only if NI_v3_Extensions is set in state */
|
||||
le32 owner_id;
|
||||
le32 security_id;
|
||||
le64 quota_charged;
|
||||
le64 usn;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
NTFS_UPDATE_ATIME = 1 << 0,
|
||||
NTFS_UPDATE_MTIME = 1 << 1,
|
||||
NTFS_UPDATE_CTIME = 1 << 2,
|
||||
} ntfs_time_update_flags;
|
||||
|
||||
#define NTFS_UPDATE_MCTIME (NTFS_UPDATE_MTIME | NTFS_UPDATE_CTIME)
|
||||
#define NTFS_UPDATE_AMCTIME (NTFS_UPDATE_ATIME | NTFS_UPDATE_MCTIME)
|
||||
|
||||
extern ntfs_inode *ntfs_inode_base(ntfs_inode *ni);
|
||||
|
||||
extern ntfs_inode *ntfs_inode_allocate(ntfs_volume *vol);
|
||||
|
||||
extern ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref);
|
||||
|
||||
extern int ntfs_inode_close(ntfs_inode *ni);
|
||||
extern int ntfs_inode_close_in_dir(ntfs_inode *ni, ntfs_inode *dir_ni);
|
||||
|
||||
#if CACHE_NIDATA_SIZE
|
||||
|
||||
struct CACHED_GENERIC;
|
||||
|
||||
extern int ntfs_inode_real_close(ntfs_inode *ni);
|
||||
extern void ntfs_inode_invalidate(ntfs_volume *vol, const MFT_REF mref);
|
||||
extern void ntfs_inode_nidata_free(const struct CACHED_GENERIC *cached);
|
||||
extern int ntfs_inode_nidata_hash(const struct CACHED_GENERIC *item);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
extern ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni,
|
||||
const MFT_REF mref);
|
||||
|
||||
extern int ntfs_inode_attach_all_extents(ntfs_inode *ni);
|
||||
|
||||
extern void ntfs_inode_mark_dirty(ntfs_inode *ni);
|
||||
|
||||
extern void ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask);
|
||||
|
||||
extern int ntfs_inode_sync(ntfs_inode *ni);
|
||||
|
||||
extern int ntfs_inode_add_attrlist(ntfs_inode *ni);
|
||||
|
||||
extern int ntfs_inode_free_space(ntfs_inode *ni, int size);
|
||||
|
||||
extern int ntfs_inode_badclus_bad(u64 mft_no, ATTR_RECORD *a);
|
||||
|
||||
extern int ntfs_inode_get_times(ntfs_inode *ni, char *value, size_t size);
|
||||
|
||||
extern int ntfs_inode_set_times(ntfs_inode *ni, const char *value,
|
||||
size_t size, int flags);
|
||||
|
||||
/* debugging */
|
||||
#define debug_double_inode(num, type)
|
||||
#define debug_cached_inode(ni)
|
||||
|
||||
#endif /* defined _NTFS_INODE_H */
|
2661
source/layout.h
Normal file
2661
source/layout.h
Normal file
File diff suppressed because it is too large
Load Diff
771
source/lcnalloc.c
Normal file
771
source/lcnalloc.c
Normal file
@ -0,0 +1,771 @@
|
||||
/**
|
||||
* lcnalloc.c - Cluster (de)allocation code. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2002-2004 Anton Altaparmakov
|
||||
* Copyright (c) 2004 Yura Pakhuchiy
|
||||
* Copyright (c) 2004-2008 Szabolcs Szakacsits
|
||||
* Copyright (c) 2008-2009 Jean-Pierre Andre
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "attrib.h"
|
||||
#include "bitmap.h"
|
||||
#include "debug.h"
|
||||
#include "runlist.h"
|
||||
#include "volume.h"
|
||||
#include "lcnalloc.h"
|
||||
#include "logging.h"
|
||||
#include "misc.h"
|
||||
|
||||
/*
|
||||
* Plenty possibilities for big optimizations all over in the cluster
|
||||
* allocation, however at the moment the dominant bottleneck (~ 90%) is
|
||||
* the update of the mapping pairs which converges to the cubic Faulhaber's
|
||||
* formula as the function of the number of extents (fragments, runs).
|
||||
*/
|
||||
#define NTFS_LCNALLOC_BSIZE 4096
|
||||
#define NTFS_LCNALLOC_SKIP NTFS_LCNALLOC_BSIZE
|
||||
|
||||
enum {
|
||||
ZONE_MFT = 1,
|
||||
ZONE_DATA1 = 2,
|
||||
ZONE_DATA2 = 4
|
||||
} ;
|
||||
|
||||
static void ntfs_cluster_set_zone_pos(LCN start, LCN end, LCN *pos, LCN tc)
|
||||
{
|
||||
ntfs_log_trace("pos: %lld tc: %lld\n", (long long)*pos, (long long)tc);
|
||||
|
||||
if (tc >= end)
|
||||
*pos = start;
|
||||
else if (tc >= start)
|
||||
*pos = tc;
|
||||
}
|
||||
|
||||
static void ntfs_cluster_update_zone_pos(ntfs_volume *vol, u8 zone, LCN tc)
|
||||
{
|
||||
ntfs_log_trace("tc = %lld, zone = %d\n", (long long)tc, zone);
|
||||
|
||||
if (zone == ZONE_MFT)
|
||||
ntfs_cluster_set_zone_pos(vol->mft_lcn, vol->mft_zone_end,
|
||||
&vol->mft_zone_pos, tc);
|
||||
else if (zone == ZONE_DATA1)
|
||||
ntfs_cluster_set_zone_pos(vol->mft_zone_end, vol->nr_clusters,
|
||||
&vol->data1_zone_pos, tc);
|
||||
else /* zone == ZONE_DATA2 */
|
||||
ntfs_cluster_set_zone_pos(0, vol->mft_zone_start,
|
||||
&vol->data2_zone_pos, tc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unmark full zones when a cluster has been freed in a full zone
|
||||
*
|
||||
* Next allocation will reuse the freed cluster
|
||||
*/
|
||||
|
||||
static void update_full_status(ntfs_volume *vol, LCN lcn)
|
||||
{
|
||||
if (lcn >= vol->mft_zone_end) {
|
||||
if (vol->full_zones & ZONE_DATA1) {
|
||||
ntfs_cluster_update_zone_pos(vol, ZONE_DATA1, lcn);
|
||||
vol->full_zones &= ~ZONE_DATA1;
|
||||
}
|
||||
} else
|
||||
if (lcn < vol->mft_zone_start) {
|
||||
if (vol->full_zones & ZONE_DATA2) {
|
||||
ntfs_cluster_update_zone_pos(vol, ZONE_DATA2, lcn);
|
||||
vol->full_zones &= ~ZONE_DATA2;
|
||||
}
|
||||
} else {
|
||||
if (vol->full_zones & ZONE_MFT) {
|
||||
ntfs_cluster_update_zone_pos(vol, ZONE_MFT, lcn);
|
||||
vol->full_zones &= ~ZONE_MFT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static s64 max_empty_bit_range(unsigned char *buf, int size)
|
||||
{
|
||||
int i, j, run = 0;
|
||||
int max_range = 0;
|
||||
s64 start_pos = -1;
|
||||
|
||||
ntfs_log_trace("Entering\n");
|
||||
|
||||
i = 0;
|
||||
while (i < size) {
|
||||
switch (*buf) {
|
||||
case 0 :
|
||||
do {
|
||||
buf++;
|
||||
run += 8;
|
||||
i++;
|
||||
} while ((i < size) && !*buf);
|
||||
break;
|
||||
case 255 :
|
||||
if (run > max_range) {
|
||||
max_range = run;
|
||||
start_pos = (s64)i * 8 - run;
|
||||
}
|
||||
run = 0;
|
||||
do {
|
||||
buf++;
|
||||
i++;
|
||||
} while ((i < size) && (*buf == 255));
|
||||
break;
|
||||
default :
|
||||
for (j = 0; j < 8; j++) {
|
||||
|
||||
int bit = *buf & (1 << j);
|
||||
|
||||
if (bit) {
|
||||
if (run > max_range) {
|
||||
max_range = run;
|
||||
start_pos = (s64)i * 8 + (j - run);
|
||||
}
|
||||
run = 0;
|
||||
} else
|
||||
run++;
|
||||
}
|
||||
i++;
|
||||
buf++;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (run > max_range)
|
||||
start_pos = (s64)i * 8 - run;
|
||||
|
||||
return start_pos;
|
||||
}
|
||||
|
||||
static int bitmap_writeback(ntfs_volume *vol, s64 pos, s64 size, void *b,
|
||||
u8 *writeback)
|
||||
{
|
||||
s64 written;
|
||||
|
||||
ntfs_log_trace("Entering\n");
|
||||
|
||||
if (!*writeback)
|
||||
return 0;
|
||||
|
||||
*writeback = 0;
|
||||
|
||||
written = ntfs_attr_pwrite(vol->lcnbmp_na, pos, size, b);
|
||||
if (written != size) {
|
||||
if (!written)
|
||||
errno = EIO;
|
||||
ntfs_log_perror("Bitmap write error (%lld, %lld)",
|
||||
(long long)pos, (long long)size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_cluster_alloc - allocate clusters on an ntfs volume
|
||||
* @vol: mounted ntfs volume on which to allocate the clusters
|
||||
* @start_vcn: vcn to use for the first allocated cluster
|
||||
* @count: number of clusters to allocate
|
||||
* @start_lcn: starting lcn at which to allocate the clusters (or -1 if none)
|
||||
* @zone: zone from which to allocate the clusters
|
||||
*
|
||||
* Allocate @count clusters preferably starting at cluster @start_lcn or at the
|
||||
* current allocator position if @start_lcn is -1, on the mounted ntfs volume
|
||||
* @vol. @zone is either DATA_ZONE for allocation of normal clusters and
|
||||
* MFT_ZONE for allocation of clusters for the master file table, i.e. the
|
||||
* $MFT/$DATA attribute.
|
||||
*
|
||||
* On success return a runlist describing the allocated cluster(s).
|
||||
*
|
||||
* On error return NULL with errno set to the error code.
|
||||
*
|
||||
* Notes on the allocation algorithm
|
||||
* =================================
|
||||
*
|
||||
* There are two data zones. First is the area between the end of the mft zone
|
||||
* and the end of the volume, and second is the area between the start of the
|
||||
* volume and the start of the mft zone. On unmodified/standard NTFS 1.x
|
||||
* volumes, the second data zone doesn't exist due to the mft zone being
|
||||
* expanded to cover the start of the volume in order to reserve space for the
|
||||
* mft bitmap attribute.
|
||||
*
|
||||
* The complexity stems from the need of implementing the mft vs data zoned
|
||||
* approach and from the fact that we have access to the lcn bitmap via up to
|
||||
* NTFS_LCNALLOC_BSIZE bytes at a time, so we need to cope with crossing over
|
||||
* boundaries of two buffers. Further, the fact that the allocator allows for
|
||||
* caller supplied hints as to the location of where allocation should begin
|
||||
* and the fact that the allocator keeps track of where in the data zones the
|
||||
* next natural allocation should occur, contribute to the complexity of the
|
||||
* function. But it should all be worthwhile, because this allocator:
|
||||
* 1) implements MFT zone reservation
|
||||
* 2) causes reduction in fragmentation.
|
||||
* The code is not optimized for speed.
|
||||
*/
|
||||
runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count,
|
||||
LCN start_lcn, const NTFS_CLUSTER_ALLOCATION_ZONES zone)
|
||||
{
|
||||
LCN zone_start, zone_end; /* current search range */
|
||||
LCN last_read_pos, lcn;
|
||||
LCN bmp_pos; /* current bit position inside the bitmap */
|
||||
LCN prev_lcn = 0, prev_run_len = 0;
|
||||
s64 clusters, br;
|
||||
runlist *rl = NULL, *trl;
|
||||
u8 *buf, *byte, bit, writeback;
|
||||
u8 pass = 1; /* 1: inside zone; 2: start of zone */
|
||||
u8 search_zone; /* 4: data2 (start) 1: mft (middle) 2: data1 (end) */
|
||||
u8 done_zones = 0;
|
||||
u8 has_guess, used_zone_pos;
|
||||
int err = 0, rlpos, rlsize, buf_size;
|
||||
|
||||
ntfs_log_enter("Entering with count = 0x%llx, start_lcn = 0x%llx, "
|
||||
"zone = %s_ZONE.\n", (long long)count, (long long)
|
||||
start_lcn, zone == MFT_ZONE ? "MFT" : "DATA");
|
||||
|
||||
if (!vol || count < 0 || start_lcn < -1 || !vol->lcnbmp_na ||
|
||||
(s8)zone < FIRST_ZONE || zone > LAST_ZONE) {
|
||||
errno = EINVAL;
|
||||
ntfs_log_perror("%s: vcn: %lld, count: %lld, lcn: %lld",
|
||||
__FUNCTION__, (long long)start_vcn,
|
||||
(long long)count, (long long)start_lcn);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Return empty runlist if @count == 0 */
|
||||
if (!count) {
|
||||
rl = ntfs_malloc(0x1000);
|
||||
if (rl) {
|
||||
rl[0].vcn = start_vcn;
|
||||
rl[0].lcn = LCN_RL_NOT_MAPPED;
|
||||
rl[0].length = 0;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
buf = ntfs_malloc(NTFS_LCNALLOC_BSIZE);
|
||||
if (!buf)
|
||||
goto out;
|
||||
/*
|
||||
* If no @start_lcn was requested, use the current zone
|
||||
* position otherwise use the requested @start_lcn.
|
||||
*/
|
||||
has_guess = 1;
|
||||
zone_start = start_lcn;
|
||||
|
||||
if (zone_start < 0) {
|
||||
if (zone == DATA_ZONE)
|
||||
zone_start = vol->data1_zone_pos;
|
||||
else
|
||||
zone_start = vol->mft_zone_pos;
|
||||
has_guess = 0;
|
||||
}
|
||||
|
||||
used_zone_pos = has_guess ? 0 : 1;
|
||||
|
||||
if (!zone_start || zone_start == vol->mft_zone_start ||
|
||||
zone_start == vol->mft_zone_end)
|
||||
pass = 2;
|
||||
|
||||
if (zone_start < vol->mft_zone_start) {
|
||||
zone_end = vol->mft_zone_start;
|
||||
search_zone = ZONE_DATA2;
|
||||
} else if (zone_start < vol->mft_zone_end) {
|
||||
zone_end = vol->mft_zone_end;
|
||||
search_zone = ZONE_MFT;
|
||||
} else {
|
||||
zone_end = vol->nr_clusters;
|
||||
search_zone = ZONE_DATA1;
|
||||
}
|
||||
|
||||
bmp_pos = zone_start;
|
||||
|
||||
/* Loop until all clusters are allocated. */
|
||||
clusters = count;
|
||||
rlpos = rlsize = 0;
|
||||
while (1) {
|
||||
/* check whether we have exhausted the current zone */
|
||||
if (search_zone & vol->full_zones)
|
||||
goto zone_pass_done;
|
||||
last_read_pos = bmp_pos >> 3;
|
||||
br = ntfs_attr_pread(vol->lcnbmp_na, last_read_pos,
|
||||
NTFS_LCNALLOC_BSIZE, buf);
|
||||
if (br <= 0) {
|
||||
if (!br)
|
||||
goto zone_pass_done;
|
||||
err = errno;
|
||||
ntfs_log_perror("Reading $BITMAP failed");
|
||||
goto err_ret;
|
||||
}
|
||||
/*
|
||||
* We might have read less than NTFS_LCNALLOC_BSIZE bytes
|
||||
* if we are close to the end of the attribute.
|
||||
*/
|
||||
buf_size = (int)br << 3;
|
||||
lcn = bmp_pos & 7;
|
||||
bmp_pos &= ~7;
|
||||
writeback = 0;
|
||||
|
||||
while (lcn < buf_size) {
|
||||
byte = buf + (lcn >> 3);
|
||||
bit = 1 << (lcn & 7);
|
||||
if (has_guess) {
|
||||
if (*byte & bit) {
|
||||
has_guess = 0;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
lcn = max_empty_bit_range(buf, br);
|
||||
if (lcn < 0)
|
||||
break;
|
||||
has_guess = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* First free bit is at lcn + bmp_pos. */
|
||||
|
||||
/* Reallocate memory if necessary. */
|
||||
if ((rlpos + 2) * (int)sizeof(runlist) >= rlsize) {
|
||||
rlsize += 4096;
|
||||
trl = realloc(rl, rlsize);
|
||||
if (!trl) {
|
||||
err = ENOMEM;
|
||||
ntfs_log_perror("realloc() failed");
|
||||
goto wb_err_ret;
|
||||
}
|
||||
rl = trl;
|
||||
}
|
||||
|
||||
/* Allocate the bitmap bit. */
|
||||
*byte |= bit;
|
||||
writeback = 1;
|
||||
if (vol->free_clusters <= 0)
|
||||
ntfs_log_error("Non-positive free clusters "
|
||||
"(%lld)!\n",
|
||||
(long long)vol->free_clusters);
|
||||
else
|
||||
vol->free_clusters--;
|
||||
|
||||
/*
|
||||
* Coalesce with previous run if adjacent LCNs.
|
||||
* Otherwise, append a new run.
|
||||
*/
|
||||
if (prev_lcn == lcn + bmp_pos - prev_run_len && rlpos) {
|
||||
ntfs_log_debug("Cluster coalesce: prev_lcn: "
|
||||
"%lld lcn: %lld bmp_pos: %lld "
|
||||
"prev_run_len: %lld\n",
|
||||
(long long)prev_lcn,
|
||||
(long long)lcn, (long long)bmp_pos,
|
||||
(long long)prev_run_len);
|
||||
rl[rlpos - 1].length = ++prev_run_len;
|
||||
} else {
|
||||
if (rlpos)
|
||||
rl[rlpos].vcn = rl[rlpos - 1].vcn +
|
||||
prev_run_len;
|
||||
else {
|
||||
rl[rlpos].vcn = start_vcn;
|
||||
ntfs_log_debug("Start_vcn: %lld\n",
|
||||
(long long)start_vcn);
|
||||
}
|
||||
|
||||
rl[rlpos].lcn = prev_lcn = lcn + bmp_pos;
|
||||
rl[rlpos].length = prev_run_len = 1;
|
||||
rlpos++;
|
||||
}
|
||||
|
||||
ntfs_log_debug("RUN: %-16lld %-16lld %-16lld\n",
|
||||
(long long)rl[rlpos - 1].vcn,
|
||||
(long long)rl[rlpos - 1].lcn,
|
||||
(long long)rl[rlpos - 1].length);
|
||||
/* Done? */
|
||||
if (!--clusters) {
|
||||
if (used_zone_pos)
|
||||
ntfs_cluster_update_zone_pos(vol,
|
||||
search_zone, lcn + bmp_pos + 1 +
|
||||
NTFS_LCNALLOC_SKIP);
|
||||
goto done_ret;
|
||||
}
|
||||
|
||||
lcn++;
|
||||
}
|
||||
|
||||
if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback)) {
|
||||
err = errno;
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
if (!used_zone_pos) {
|
||||
|
||||
used_zone_pos = 1;
|
||||
|
||||
if (search_zone == ZONE_MFT)
|
||||
zone_start = vol->mft_zone_pos;
|
||||
else if (search_zone == ZONE_DATA1)
|
||||
zone_start = vol->data1_zone_pos;
|
||||
else
|
||||
zone_start = vol->data2_zone_pos;
|
||||
|
||||
if (!zone_start || zone_start == vol->mft_zone_start ||
|
||||
zone_start == vol->mft_zone_end)
|
||||
pass = 2;
|
||||
bmp_pos = zone_start;
|
||||
} else
|
||||
bmp_pos += buf_size;
|
||||
|
||||
if (bmp_pos < zone_end)
|
||||
continue;
|
||||
|
||||
zone_pass_done:
|
||||
ntfs_log_trace("Finished current zone pass(%i).\n", pass);
|
||||
if (pass == 1) {
|
||||
pass = 2;
|
||||
zone_end = zone_start;
|
||||
|
||||
if (search_zone == ZONE_MFT)
|
||||
zone_start = vol->mft_zone_start;
|
||||
else if (search_zone == ZONE_DATA1)
|
||||
zone_start = vol->mft_zone_end;
|
||||
else
|
||||
zone_start = 0;
|
||||
|
||||
/* Sanity check. */
|
||||
if (zone_end < zone_start)
|
||||
zone_end = zone_start;
|
||||
|
||||
bmp_pos = zone_start;
|
||||
|
||||
continue;
|
||||
}
|
||||
/* pass == 2 */
|
||||
done_zones_check:
|
||||
done_zones |= search_zone;
|
||||
vol->full_zones |= search_zone;
|
||||
if (done_zones < (ZONE_MFT + ZONE_DATA1 + ZONE_DATA2)) {
|
||||
ntfs_log_trace("Switching zone.\n");
|
||||
pass = 1;
|
||||
if (rlpos) {
|
||||
LCN tc = rl[rlpos - 1].lcn +
|
||||
rl[rlpos - 1].length + NTFS_LCNALLOC_SKIP;
|
||||
|
||||
if (used_zone_pos)
|
||||
ntfs_cluster_update_zone_pos(vol,
|
||||
search_zone, tc);
|
||||
}
|
||||
|
||||
switch (search_zone) {
|
||||
case ZONE_MFT:
|
||||
ntfs_log_trace("Zone switch: mft -> data1\n");
|
||||
switch_to_data1_zone: search_zone = ZONE_DATA1;
|
||||
zone_start = vol->data1_zone_pos;
|
||||
zone_end = vol->nr_clusters;
|
||||
if (zone_start == vol->mft_zone_end)
|
||||
pass = 2;
|
||||
break;
|
||||
case ZONE_DATA1:
|
||||
ntfs_log_trace("Zone switch: data1 -> data2\n");
|
||||
search_zone = ZONE_DATA2;
|
||||
zone_start = vol->data2_zone_pos;
|
||||
zone_end = vol->mft_zone_start;
|
||||
if (!zone_start)
|
||||
pass = 2;
|
||||
break;
|
||||
case ZONE_DATA2:
|
||||
if (!(done_zones & ZONE_DATA1)) {
|
||||
ntfs_log_trace("data2 -> data1\n");
|
||||
goto switch_to_data1_zone;
|
||||
}
|
||||
ntfs_log_trace("Zone switch: data2 -> mft\n");
|
||||
search_zone = ZONE_MFT;
|
||||
zone_start = vol->mft_zone_pos;
|
||||
zone_end = vol->mft_zone_end;
|
||||
if (zone_start == vol->mft_zone_start)
|
||||
pass = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
bmp_pos = zone_start;
|
||||
|
||||
if (zone_start == zone_end) {
|
||||
ntfs_log_trace("Empty zone, skipped.\n");
|
||||
goto done_zones_check;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
ntfs_log_trace("All zones are finished, no space on device.\n");
|
||||
err = ENOSPC;
|
||||
goto err_ret;
|
||||
}
|
||||
done_ret:
|
||||
ntfs_log_debug("At done_ret.\n");
|
||||
/* Add runlist terminator element. */
|
||||
rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length;
|
||||
rl[rlpos].lcn = LCN_RL_NOT_MAPPED;
|
||||
rl[rlpos].length = 0;
|
||||
if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback)) {
|
||||
err = errno;
|
||||
goto err_ret;
|
||||
}
|
||||
done_err_ret:
|
||||
free(buf);
|
||||
if (err) {
|
||||
errno = err;
|
||||
ntfs_log_perror("Failed to allocate clusters");
|
||||
rl = NULL;
|
||||
}
|
||||
out:
|
||||
ntfs_log_leave("\n");
|
||||
return rl;
|
||||
|
||||
wb_err_ret:
|
||||
ntfs_log_trace("At wb_err_ret.\n");
|
||||
if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback))
|
||||
err = errno;
|
||||
err_ret:
|
||||
ntfs_log_trace("At err_ret.\n");
|
||||
if (rl) {
|
||||
/* Add runlist terminator element. */
|
||||
rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length;
|
||||
rl[rlpos].lcn = LCN_RL_NOT_MAPPED;
|
||||
rl[rlpos].length = 0;
|
||||
ntfs_debug_runlist_dump(rl);
|
||||
ntfs_cluster_free_from_rl(vol, rl);
|
||||
free(rl);
|
||||
rl = NULL;
|
||||
}
|
||||
goto done_err_ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_cluster_free_from_rl - free clusters from runlist
|
||||
* @vol: mounted ntfs volume on which to free the clusters
|
||||
* @rl: runlist from which deallocate clusters
|
||||
*
|
||||
* On success return 0 and on error return -1 with errno set to the error code.
|
||||
*/
|
||||
int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl)
|
||||
{
|
||||
s64 nr_freed = 0;
|
||||
int ret = -1;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
|
||||
for (; rl->length; rl++) {
|
||||
|
||||
ntfs_log_trace("Dealloc lcn 0x%llx, len 0x%llx.\n",
|
||||
(long long)rl->lcn, (long long)rl->length);
|
||||
|
||||
if (rl->lcn >= 0) {
|
||||
update_full_status(vol,rl->lcn);
|
||||
if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn,
|
||||
rl->length)) {
|
||||
ntfs_log_perror("Cluster deallocation failed "
|
||||
"(%lld, %lld)",
|
||||
(long long)rl->lcn,
|
||||
(long long)rl->length);
|
||||
goto out;
|
||||
}
|
||||
nr_freed += rl->length ;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
vol->free_clusters += nr_freed;
|
||||
if (vol->free_clusters > vol->nr_clusters)
|
||||
ntfs_log_error("Too many free clusters (%lld > %lld)!",
|
||||
(long long)vol->free_clusters,
|
||||
(long long)vol->nr_clusters);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Basic cluster run free
|
||||
* Returns 0 if successful
|
||||
*/
|
||||
|
||||
int ntfs_cluster_free_basic(ntfs_volume *vol, s64 lcn, s64 count)
|
||||
{
|
||||
s64 nr_freed = 0;
|
||||
int ret = -1;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
ntfs_log_trace("Dealloc lcn 0x%llx, len 0x%llx.\n",
|
||||
(long long)lcn, (long long)count);
|
||||
|
||||
if (lcn >= 0) {
|
||||
update_full_status(vol,lcn);
|
||||
if (ntfs_bitmap_clear_run(vol->lcnbmp_na, lcn,
|
||||
count)) {
|
||||
ntfs_log_perror("Cluster deallocation failed "
|
||||
"(%lld, %lld)",
|
||||
(long long)lcn,
|
||||
(long long)count);
|
||||
goto out;
|
||||
}
|
||||
nr_freed += count;
|
||||
}
|
||||
ret = 0;
|
||||
out:
|
||||
vol->free_clusters += nr_freed;
|
||||
if (vol->free_clusters > vol->nr_clusters)
|
||||
ntfs_log_error("Too many free clusters (%lld > %lld)!",
|
||||
(long long)vol->free_clusters,
|
||||
(long long)vol->nr_clusters);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_cluster_free - free clusters on an ntfs volume
|
||||
* @vol: mounted ntfs volume on which to free the clusters
|
||||
* @na: attribute whose runlist describes the clusters to free
|
||||
* @start_vcn: vcn in @rl at which to start freeing clusters
|
||||
* @count: number of clusters to free or -1 for all clusters
|
||||
*
|
||||
* Free @count clusters starting at the cluster @start_vcn in the runlist
|
||||
* described by the attribute @na from the mounted ntfs volume @vol.
|
||||
*
|
||||
* If @count is -1, all clusters from @start_vcn to the end of the runlist
|
||||
* are deallocated.
|
||||
*
|
||||
* On success return the number of deallocated clusters (not counting sparse
|
||||
* clusters) and on error return -1 with errno set to the error code.
|
||||
*/
|
||||
int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count)
|
||||
{
|
||||
runlist *rl;
|
||||
s64 delta, to_free, nr_freed = 0;
|
||||
int ret = -1;
|
||||
|
||||
if (!vol || !vol->lcnbmp_na || !na || start_vcn < 0 ||
|
||||
(count < 0 && count != -1)) {
|
||||
ntfs_log_trace("Invalid arguments!\n");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ntfs_log_enter("Entering for inode 0x%llx, attr 0x%x, count 0x%llx, "
|
||||
"vcn 0x%llx.\n", (unsigned long long)na->ni->mft_no,
|
||||
na->type, (long long)count, (long long)start_vcn);
|
||||
|
||||
rl = ntfs_attr_find_vcn(na, start_vcn);
|
||||
if (!rl) {
|
||||
if (errno == ENOENT)
|
||||
ret = 0;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (rl->lcn < 0 && rl->lcn != LCN_HOLE) {
|
||||
errno = EIO;
|
||||
ntfs_log_perror("%s: Unexpected lcn (%lld)", __FUNCTION__,
|
||||
(long long)rl->lcn);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Find the starting cluster inside the run that needs freeing. */
|
||||
delta = start_vcn - rl->vcn;
|
||||
|
||||
/* The number of clusters in this run that need freeing. */
|
||||
to_free = rl->length - delta;
|
||||
if (count >= 0 && to_free > count)
|
||||
to_free = count;
|
||||
|
||||
if (rl->lcn != LCN_HOLE) {
|
||||
/* Do the actual freeing of the clusters in this run. */
|
||||
update_full_status(vol,rl->lcn + delta);
|
||||
if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn + delta,
|
||||
to_free))
|
||||
goto leave;
|
||||
nr_freed = to_free;
|
||||
}
|
||||
|
||||
/* Go to the next run and adjust the number of clusters left to free. */
|
||||
++rl;
|
||||
if (count >= 0)
|
||||
count -= to_free;
|
||||
|
||||
/*
|
||||
* Loop over the remaining runs, using @count as a capping value, and
|
||||
* free them.
|
||||
*/
|
||||
for (; rl->length && count != 0; ++rl) {
|
||||
// FIXME: Need to try ntfs_attr_map_runlist() for attribute
|
||||
// list support! (AIA)
|
||||
if (rl->lcn < 0 && rl->lcn != LCN_HOLE) {
|
||||
// FIXME: Eeek! We need rollback! (AIA)
|
||||
errno = EIO;
|
||||
ntfs_log_perror("%s: Invalid lcn (%lli)",
|
||||
__FUNCTION__, (long long)rl->lcn);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* The number of clusters in this run that need freeing. */
|
||||
to_free = rl->length;
|
||||
if (count >= 0 && to_free > count)
|
||||
to_free = count;
|
||||
|
||||
if (rl->lcn != LCN_HOLE) {
|
||||
update_full_status(vol,rl->lcn);
|
||||
if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn,
|
||||
to_free)) {
|
||||
// FIXME: Eeek! We need rollback! (AIA)
|
||||
ntfs_log_perror("%s: Clearing bitmap run failed",
|
||||
__FUNCTION__);
|
||||
goto out;
|
||||
}
|
||||
nr_freed += to_free;
|
||||
}
|
||||
|
||||
if (count >= 0)
|
||||
count -= to_free;
|
||||
}
|
||||
|
||||
if (count != -1 && count != 0) {
|
||||
// FIXME: Eeek! BUG()
|
||||
errno = EIO;
|
||||
ntfs_log_perror("%s: count still not zero (%lld)", __FUNCTION__,
|
||||
(long long)count);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = nr_freed;
|
||||
out:
|
||||
vol->free_clusters += nr_freed ;
|
||||
if (vol->free_clusters > vol->nr_clusters)
|
||||
ntfs_log_error("Too many free clusters (%lld > %lld)!",
|
||||
(long long)vol->free_clusters,
|
||||
(long long)vol->nr_clusters);
|
||||
leave:
|
||||
ntfs_log_leave("\n");
|
||||
return ret;
|
||||
}
|
51
source/lcnalloc.h
Normal file
51
source/lcnalloc.h
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* lcnalloc.h - Exports for cluster (de)allocation. Originated from the Linux-NTFS
|
||||
* project.
|
||||
*
|
||||
* Copyright (c) 2002 Anton Altaparmakov
|
||||
* Copyright (c) 2004 Yura Pakhuchiy
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_LCNALLOC_H
|
||||
#define _NTFS_LCNALLOC_H
|
||||
|
||||
#include "types.h"
|
||||
#include "runlist.h"
|
||||
#include "volume.h"
|
||||
|
||||
/**
|
||||
* enum NTFS_CLUSTER_ALLOCATION_ZONES -
|
||||
*/
|
||||
typedef enum {
|
||||
FIRST_ZONE = 0, /* For sanity checking. */
|
||||
MFT_ZONE = 0, /* Allocate from $MFT zone. */
|
||||
DATA_ZONE = 1, /* Allocate from $DATA zone. */
|
||||
LAST_ZONE = 1, /* For sanity checking. */
|
||||
} NTFS_CLUSTER_ALLOCATION_ZONES;
|
||||
|
||||
extern runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count,
|
||||
LCN start_lcn, const NTFS_CLUSTER_ALLOCATION_ZONES zone);
|
||||
|
||||
extern int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl);
|
||||
extern int ntfs_cluster_free_basic(ntfs_volume *vol, s64 lcn, s64 count);
|
||||
|
||||
extern int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn,
|
||||
s64 count);
|
||||
|
||||
#endif /* defined _NTFS_LCNALLOC_H */
|
||||
|
737
source/logfile.c
Normal file
737
source/logfile.c
Normal file
@ -0,0 +1,737 @@
|
||||
/**
|
||||
* logfile.c - NTFS journal handling. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2002-2005 Anton Altaparmakov
|
||||
* Copyright (c) 2005 Yura Pakhuchiy
|
||||
* Copyright (c) 2005-2009 Szabolcs Szakacsits
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "attrib.h"
|
||||
#include "debug.h"
|
||||
#include "logfile.h"
|
||||
#include "volume.h"
|
||||
#include "mst.h"
|
||||
#include "logging.h"
|
||||
#include "misc.h"
|
||||
|
||||
/**
|
||||
* ntfs_check_restart_page_header - check the page header for consistency
|
||||
* @rp: restart page header to check
|
||||
* @pos: position in logfile at which the restart page header resides
|
||||
*
|
||||
* Check the restart page header @rp for consistency and return TRUE if it is
|
||||
* consistent and FALSE otherwise.
|
||||
*
|
||||
* This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
|
||||
* require the full restart page.
|
||||
*/
|
||||
static BOOL ntfs_check_restart_page_header(RESTART_PAGE_HEADER *rp, s64 pos)
|
||||
{
|
||||
u32 logfile_system_page_size, logfile_log_page_size;
|
||||
u16 ra_ofs, usa_count, usa_ofs, usa_end = 0;
|
||||
BOOL have_usa = TRUE;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
/*
|
||||
* If the system or log page sizes are smaller than the ntfs block size
|
||||
* or either is not a power of 2 we cannot handle this log file.
|
||||
*/
|
||||
logfile_system_page_size = le32_to_cpu(rp->system_page_size);
|
||||
logfile_log_page_size = le32_to_cpu(rp->log_page_size);
|
||||
if (logfile_system_page_size < NTFS_BLOCK_SIZE ||
|
||||
logfile_log_page_size < NTFS_BLOCK_SIZE ||
|
||||
logfile_system_page_size &
|
||||
(logfile_system_page_size - 1) ||
|
||||
logfile_log_page_size & (logfile_log_page_size - 1)) {
|
||||
ntfs_log_error("$LogFile uses unsupported page size.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/*
|
||||
* We must be either at !pos (1st restart page) or at pos = system page
|
||||
* size (2nd restart page).
|
||||
*/
|
||||
if (pos && pos != logfile_system_page_size) {
|
||||
ntfs_log_error("Found restart area in incorrect "
|
||||
"position in $LogFile.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/* We only know how to handle version 1.1. */
|
||||
if (sle16_to_cpu(rp->major_ver) != 1 ||
|
||||
sle16_to_cpu(rp->minor_ver) != 1) {
|
||||
ntfs_log_error("$LogFile version %i.%i is not "
|
||||
"supported. (This driver supports version "
|
||||
"1.1 only.)\n", (int)sle16_to_cpu(rp->major_ver),
|
||||
(int)sle16_to_cpu(rp->minor_ver));
|
||||
return FALSE;
|
||||
}
|
||||
/*
|
||||
* If chkdsk has been run the restart page may not be protected by an
|
||||
* update sequence array.
|
||||
*/
|
||||
if (ntfs_is_chkd_record(rp->magic) && !le16_to_cpu(rp->usa_count)) {
|
||||
have_usa = FALSE;
|
||||
goto skip_usa_checks;
|
||||
}
|
||||
/* Verify the size of the update sequence array. */
|
||||
usa_count = 1 + (logfile_system_page_size >> NTFS_BLOCK_SIZE_BITS);
|
||||
if (usa_count != le16_to_cpu(rp->usa_count)) {
|
||||
ntfs_log_error("$LogFile restart page specifies "
|
||||
"inconsistent update sequence array count.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/* Verify the position of the update sequence array. */
|
||||
usa_ofs = le16_to_cpu(rp->usa_ofs);
|
||||
usa_end = usa_ofs + usa_count * sizeof(u16);
|
||||
if (usa_ofs < sizeof(RESTART_PAGE_HEADER) ||
|
||||
usa_end > NTFS_BLOCK_SIZE - sizeof(u16)) {
|
||||
ntfs_log_error("$LogFile restart page specifies "
|
||||
"inconsistent update sequence array offset.\n");
|
||||
return FALSE;
|
||||
}
|
||||
skip_usa_checks:
|
||||
/*
|
||||
* Verify the position of the restart area. It must be:
|
||||
* - aligned to 8-byte boundary,
|
||||
* - after the update sequence array, and
|
||||
* - within the system page size.
|
||||
*/
|
||||
ra_ofs = le16_to_cpu(rp->restart_area_offset);
|
||||
if (ra_ofs & 7 || (have_usa ? ra_ofs < usa_end :
|
||||
ra_ofs < sizeof(RESTART_PAGE_HEADER)) ||
|
||||
ra_ofs > logfile_system_page_size) {
|
||||
ntfs_log_error("$LogFile restart page specifies "
|
||||
"inconsistent restart area offset.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/*
|
||||
* Only restart pages modified by chkdsk are allowed to have chkdsk_lsn
|
||||
* set.
|
||||
*/
|
||||
if (!ntfs_is_chkd_record(rp->magic) && sle64_to_cpu(rp->chkdsk_lsn)) {
|
||||
ntfs_log_error("$LogFile restart page is not modified "
|
||||
"by chkdsk but a chkdsk LSN is specified.\n");
|
||||
return FALSE;
|
||||
}
|
||||
ntfs_log_trace("Done.\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_check_restart_area - check the restart area for consistency
|
||||
* @rp: restart page whose restart area to check
|
||||
*
|
||||
* Check the restart area of the restart page @rp for consistency and return
|
||||
* TRUE if it is consistent and FALSE otherwise.
|
||||
*
|
||||
* This function assumes that the restart page header has already been
|
||||
* consistency checked.
|
||||
*
|
||||
* This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
|
||||
* require the full restart page.
|
||||
*/
|
||||
static BOOL ntfs_check_restart_area(RESTART_PAGE_HEADER *rp)
|
||||
{
|
||||
u64 file_size;
|
||||
RESTART_AREA *ra;
|
||||
u16 ra_ofs, ra_len, ca_ofs;
|
||||
u8 fs_bits;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
ra_ofs = le16_to_cpu(rp->restart_area_offset);
|
||||
ra = (RESTART_AREA*)((u8*)rp + ra_ofs);
|
||||
/*
|
||||
* Everything before ra->file_size must be before the first word
|
||||
* protected by an update sequence number. This ensures that it is
|
||||
* safe to access ra->client_array_offset.
|
||||
*/
|
||||
if (ra_ofs + offsetof(RESTART_AREA, file_size) >
|
||||
NTFS_BLOCK_SIZE - sizeof(u16)) {
|
||||
ntfs_log_error("$LogFile restart area specifies "
|
||||
"inconsistent file offset.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/*
|
||||
* Now that we can access ra->client_array_offset, make sure everything
|
||||
* up to the log client array is before the first word protected by an
|
||||
* update sequence number. This ensures we can access all of the
|
||||
* restart area elements safely. Also, the client array offset must be
|
||||
* aligned to an 8-byte boundary.
|
||||
*/
|
||||
ca_ofs = le16_to_cpu(ra->client_array_offset);
|
||||
if (((ca_ofs + 7) & ~7) != ca_ofs ||
|
||||
ra_ofs + ca_ofs > (u16)(NTFS_BLOCK_SIZE -
|
||||
sizeof(u16))) {
|
||||
ntfs_log_error("$LogFile restart area specifies "
|
||||
"inconsistent client array offset.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/*
|
||||
* The restart area must end within the system page size both when
|
||||
* calculated manually and as specified by ra->restart_area_length.
|
||||
* Also, the calculated length must not exceed the specified length.
|
||||
*/
|
||||
ra_len = ca_ofs + le16_to_cpu(ra->log_clients) *
|
||||
sizeof(LOG_CLIENT_RECORD);
|
||||
if ((u32)(ra_ofs + ra_len) > le32_to_cpu(rp->system_page_size) ||
|
||||
(u32)(ra_ofs + le16_to_cpu(ra->restart_area_length)) >
|
||||
le32_to_cpu(rp->system_page_size) ||
|
||||
ra_len > le16_to_cpu(ra->restart_area_length)) {
|
||||
ntfs_log_error("$LogFile restart area is out of bounds "
|
||||
"of the system page size specified by the "
|
||||
"restart page header and/or the specified "
|
||||
"restart area length is inconsistent.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/*
|
||||
* The ra->client_free_list and ra->client_in_use_list must be either
|
||||
* LOGFILE_NO_CLIENT or less than ra->log_clients or they are
|
||||
* overflowing the client array.
|
||||
*/
|
||||
if ((ra->client_free_list != LOGFILE_NO_CLIENT &&
|
||||
le16_to_cpu(ra->client_free_list) >=
|
||||
le16_to_cpu(ra->log_clients)) ||
|
||||
(ra->client_in_use_list != LOGFILE_NO_CLIENT &&
|
||||
le16_to_cpu(ra->client_in_use_list) >=
|
||||
le16_to_cpu(ra->log_clients))) {
|
||||
ntfs_log_error("$LogFile restart area specifies "
|
||||
"overflowing client free and/or in use lists.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/*
|
||||
* Check ra->seq_number_bits against ra->file_size for consistency.
|
||||
* We cannot just use ffs() because the file size is not a power of 2.
|
||||
*/
|
||||
file_size = (u64)sle64_to_cpu(ra->file_size);
|
||||
fs_bits = 0;
|
||||
while (file_size) {
|
||||
file_size >>= 1;
|
||||
fs_bits++;
|
||||
}
|
||||
if (le32_to_cpu(ra->seq_number_bits) != (u32)(67 - fs_bits)) {
|
||||
ntfs_log_error("$LogFile restart area specifies "
|
||||
"inconsistent sequence number bits.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/* The log record header length must be a multiple of 8. */
|
||||
if (((le16_to_cpu(ra->log_record_header_length) + 7) & ~7) !=
|
||||
le16_to_cpu(ra->log_record_header_length)) {
|
||||
ntfs_log_error("$LogFile restart area specifies "
|
||||
"inconsistent log record header length.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/* Ditto for the log page data offset. */
|
||||
if (((le16_to_cpu(ra->log_page_data_offset) + 7) & ~7) !=
|
||||
le16_to_cpu(ra->log_page_data_offset)) {
|
||||
ntfs_log_error("$LogFile restart area specifies "
|
||||
"inconsistent log page data offset.\n");
|
||||
return FALSE;
|
||||
}
|
||||
ntfs_log_trace("Done.\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_check_log_client_array - check the log client array for consistency
|
||||
* @rp: restart page whose log client array to check
|
||||
*
|
||||
* Check the log client array of the restart page @rp for consistency and
|
||||
* return TRUE if it is consistent and FALSE otherwise.
|
||||
*
|
||||
* This function assumes that the restart page header and the restart area have
|
||||
* already been consistency checked.
|
||||
*
|
||||
* Unlike ntfs_check_restart_page_header() and ntfs_check_restart_area(), this
|
||||
* function needs @rp->system_page_size bytes in @rp, i.e. it requires the full
|
||||
* restart page and the page must be multi sector transfer deprotected.
|
||||
*/
|
||||
static BOOL ntfs_check_log_client_array(RESTART_PAGE_HEADER *rp)
|
||||
{
|
||||
RESTART_AREA *ra;
|
||||
LOG_CLIENT_RECORD *ca, *cr;
|
||||
u16 nr_clients, idx;
|
||||
BOOL in_free_list, idx_is_first;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
|
||||
ca = (LOG_CLIENT_RECORD*)((u8*)ra +
|
||||
le16_to_cpu(ra->client_array_offset));
|
||||
/*
|
||||
* Check the ra->client_free_list first and then check the
|
||||
* ra->client_in_use_list. Check each of the log client records in
|
||||
* each of the lists and check that the array does not overflow the
|
||||
* ra->log_clients value. Also keep track of the number of records
|
||||
* visited as there cannot be more than ra->log_clients records and
|
||||
* that way we detect eventual loops in within a list.
|
||||
*/
|
||||
nr_clients = le16_to_cpu(ra->log_clients);
|
||||
idx = le16_to_cpu(ra->client_free_list);
|
||||
in_free_list = TRUE;
|
||||
check_list:
|
||||
for (idx_is_first = TRUE; idx != LOGFILE_NO_CLIENT_CPU; nr_clients--,
|
||||
idx = le16_to_cpu(cr->next_client)) {
|
||||
if (!nr_clients || idx >= le16_to_cpu(ra->log_clients))
|
||||
goto err_out;
|
||||
/* Set @cr to the current log client record. */
|
||||
cr = ca + idx;
|
||||
/* The first log client record must not have a prev_client. */
|
||||
if (idx_is_first) {
|
||||
if (cr->prev_client != LOGFILE_NO_CLIENT)
|
||||
goto err_out;
|
||||
idx_is_first = FALSE;
|
||||
}
|
||||
}
|
||||
/* Switch to and check the in use list if we just did the free list. */
|
||||
if (in_free_list) {
|
||||
in_free_list = FALSE;
|
||||
idx = le16_to_cpu(ra->client_in_use_list);
|
||||
goto check_list;
|
||||
}
|
||||
ntfs_log_trace("Done.\n");
|
||||
return TRUE;
|
||||
err_out:
|
||||
ntfs_log_error("$LogFile log client array is corrupt.\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_check_and_load_restart_page - check the restart page for consistency
|
||||
* @log_na: opened ntfs attribute for journal $LogFile
|
||||
* @rp: restart page to check
|
||||
* @pos: position in @log_na at which the restart page resides
|
||||
* @wrp: [OUT] copy of the multi sector transfer deprotected restart page
|
||||
* @lsn: [OUT] set to the current logfile lsn on success
|
||||
*
|
||||
* Check the restart page @rp for consistency and return 0 if it is consistent
|
||||
* and errno otherwise. The restart page may have been modified by chkdsk in
|
||||
* which case its magic is CHKD instead of RSTR.
|
||||
*
|
||||
* This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
|
||||
* require the full restart page.
|
||||
*
|
||||
* If @wrp is not NULL, on success, *@wrp will point to a buffer containing a
|
||||
* copy of the complete multi sector transfer deprotected page. On failure,
|
||||
* *@wrp is undefined.
|
||||
*
|
||||
* Similarly, if @lsn is not NULL, on success *@lsn will be set to the current
|
||||
* logfile lsn according to this restart page. On failure, *@lsn is undefined.
|
||||
*
|
||||
* The following error codes are defined:
|
||||
* EINVAL - The restart page is inconsistent.
|
||||
* ENOMEM - Not enough memory to load the restart page.
|
||||
* EIO - Failed to reading from $LogFile.
|
||||
*/
|
||||
static int ntfs_check_and_load_restart_page(ntfs_attr *log_na,
|
||||
RESTART_PAGE_HEADER *rp, s64 pos, RESTART_PAGE_HEADER **wrp,
|
||||
LSN *lsn)
|
||||
{
|
||||
RESTART_AREA *ra;
|
||||
RESTART_PAGE_HEADER *trp;
|
||||
int err;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
/* Check the restart page header for consistency. */
|
||||
if (!ntfs_check_restart_page_header(rp, pos)) {
|
||||
/* Error output already done inside the function. */
|
||||
return EINVAL;
|
||||
}
|
||||
/* Check the restart area for consistency. */
|
||||
if (!ntfs_check_restart_area(rp)) {
|
||||
/* Error output already done inside the function. */
|
||||
return EINVAL;
|
||||
}
|
||||
ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
|
||||
/*
|
||||
* Allocate a buffer to store the whole restart page so we can multi
|
||||
* sector transfer deprotect it.
|
||||
*/
|
||||
trp = ntfs_malloc(le32_to_cpu(rp->system_page_size));
|
||||
if (!trp)
|
||||
return errno;
|
||||
/*
|
||||
* Read the whole of the restart page into the buffer. If it fits
|
||||
* completely inside @rp, just copy it from there. Otherwise read it
|
||||
* from disk.
|
||||
*/
|
||||
if (le32_to_cpu(rp->system_page_size) <= NTFS_BLOCK_SIZE)
|
||||
memcpy(trp, rp, le32_to_cpu(rp->system_page_size));
|
||||
else if (ntfs_attr_pread(log_na, pos,
|
||||
le32_to_cpu(rp->system_page_size), trp) !=
|
||||
le32_to_cpu(rp->system_page_size)) {
|
||||
err = errno;
|
||||
ntfs_log_error("Failed to read whole restart page into the "
|
||||
"buffer.\n");
|
||||
if (err != ENOMEM)
|
||||
err = EIO;
|
||||
goto err_out;
|
||||
}
|
||||
/*
|
||||
* Perform the multi sector transfer deprotection on the buffer if the
|
||||
* restart page is protected.
|
||||
*/
|
||||
if ((!ntfs_is_chkd_record(trp->magic) || le16_to_cpu(trp->usa_count))
|
||||
&& ntfs_mst_post_read_fixup((NTFS_RECORD*)trp,
|
||||
le32_to_cpu(rp->system_page_size))) {
|
||||
/*
|
||||
* A multi sector tranfer error was detected. We only need to
|
||||
* abort if the restart page contents exceed the multi sector
|
||||
* transfer fixup of the first sector.
|
||||
*/
|
||||
if (le16_to_cpu(rp->restart_area_offset) +
|
||||
le16_to_cpu(ra->restart_area_length) >
|
||||
NTFS_BLOCK_SIZE - (int)sizeof(u16)) {
|
||||
ntfs_log_error("Multi sector transfer error "
|
||||
"detected in $LogFile restart page.\n");
|
||||
err = EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If the restart page is modified by chkdsk or there are no active
|
||||
* logfile clients, the logfile is consistent. Otherwise, need to
|
||||
* check the log client records for consistency, too.
|
||||
*/
|
||||
err = 0;
|
||||
if (ntfs_is_rstr_record(rp->magic) &&
|
||||
ra->client_in_use_list != LOGFILE_NO_CLIENT) {
|
||||
if (!ntfs_check_log_client_array(trp)) {
|
||||
err = EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
if (lsn) {
|
||||
if (ntfs_is_rstr_record(rp->magic))
|
||||
*lsn = sle64_to_cpu(ra->current_lsn);
|
||||
else /* if (ntfs_is_chkd_record(rp->magic)) */
|
||||
*lsn = sle64_to_cpu(rp->chkdsk_lsn);
|
||||
}
|
||||
ntfs_log_trace("Done.\n");
|
||||
if (wrp)
|
||||
*wrp = trp;
|
||||
else {
|
||||
err_out:
|
||||
free(trp);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_check_logfile - check in the journal if the volume is consistent
|
||||
* @log_na: ntfs attribute of loaded journal $LogFile to check
|
||||
* @rp: [OUT] on success this is a copy of the current restart page
|
||||
*
|
||||
* Check the $LogFile journal for consistency and return TRUE if it is
|
||||
* consistent and FALSE if not. On success, the current restart page is
|
||||
* returned in *@rp. Caller must call ntfs_free(*@rp) when finished with it.
|
||||
*
|
||||
* At present we only check the two restart pages and ignore the log record
|
||||
* pages.
|
||||
*
|
||||
* Note that the MstProtected flag is not set on the $LogFile inode and hence
|
||||
* when reading pages they are not deprotected. This is because we do not know
|
||||
* if the $LogFile was created on a system with a different page size to ours
|
||||
* yet and mst deprotection would fail if our page size is smaller.
|
||||
*/
|
||||
BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp)
|
||||
{
|
||||
s64 size, pos;
|
||||
LSN rstr1_lsn, rstr2_lsn;
|
||||
ntfs_volume *vol = log_na->ni->vol;
|
||||
u8 *kaddr = NULL;
|
||||
RESTART_PAGE_HEADER *rstr1_ph = NULL;
|
||||
RESTART_PAGE_HEADER *rstr2_ph = NULL;
|
||||
int log_page_size, log_page_mask, err;
|
||||
BOOL logfile_is_empty = TRUE;
|
||||
u8 log_page_bits;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
/* An empty $LogFile must have been clean before it got emptied. */
|
||||
if (NVolLogFileEmpty(vol))
|
||||
goto is_empty;
|
||||
size = log_na->data_size;
|
||||
/* Make sure the file doesn't exceed the maximum allowed size. */
|
||||
if (size > (s64)MaxLogFileSize)
|
||||
size = MaxLogFileSize;
|
||||
log_page_size = DefaultLogPageSize;
|
||||
log_page_mask = log_page_size - 1;
|
||||
/*
|
||||
* Use generic_ffs() instead of ffs() to enable the compiler to
|
||||
* optimize log_page_size and log_page_bits into constants.
|
||||
*/
|
||||
log_page_bits = ffs(log_page_size) - 1;
|
||||
size &= ~(log_page_size - 1);
|
||||
|
||||
/*
|
||||
* Ensure the log file is big enough to store at least the two restart
|
||||
* pages and the minimum number of log record pages.
|
||||
*/
|
||||
if (size < log_page_size * 2 || (size - log_page_size * 2) >>
|
||||
log_page_bits < MinLogRecordPages) {
|
||||
ntfs_log_error("$LogFile is too small.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/* Allocate memory for restart page. */
|
||||
kaddr = ntfs_malloc(NTFS_BLOCK_SIZE);
|
||||
if (!kaddr)
|
||||
return FALSE;
|
||||
/*
|
||||
* Read through the file looking for a restart page. Since the restart
|
||||
* page header is at the beginning of a page we only need to search at
|
||||
* what could be the beginning of a page (for each page size) rather
|
||||
* than scanning the whole file byte by byte. If all potential places
|
||||
* contain empty and uninitialized records, the log file can be assumed
|
||||
* to be empty.
|
||||
*/
|
||||
for (pos = 0; pos < size; pos <<= 1) {
|
||||
/*
|
||||
* Read first NTFS_BLOCK_SIZE bytes of potential restart page.
|
||||
*/
|
||||
if (ntfs_attr_pread(log_na, pos, NTFS_BLOCK_SIZE, kaddr) !=
|
||||
NTFS_BLOCK_SIZE) {
|
||||
ntfs_log_error("Failed to read first NTFS_BLOCK_SIZE "
|
||||
"bytes of potential restart page.\n");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
/*
|
||||
* A non-empty block means the logfile is not empty while an
|
||||
* empty block after a non-empty block has been encountered
|
||||
* means we are done.
|
||||
*/
|
||||
if (!ntfs_is_empty_recordp((le32*)kaddr))
|
||||
logfile_is_empty = FALSE;
|
||||
else if (!logfile_is_empty)
|
||||
break;
|
||||
/*
|
||||
* A log record page means there cannot be a restart page after
|
||||
* this so no need to continue searching.
|
||||
*/
|
||||
if (ntfs_is_rcrd_recordp((le32*)kaddr))
|
||||
break;
|
||||
/* If not a (modified by chkdsk) restart page, continue. */
|
||||
if (!ntfs_is_rstr_recordp((le32*)kaddr) &&
|
||||
!ntfs_is_chkd_recordp((le32*)kaddr)) {
|
||||
if (!pos)
|
||||
pos = NTFS_BLOCK_SIZE >> 1;
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* Check the (modified by chkdsk) restart page for consistency
|
||||
* and get a copy of the complete multi sector transfer
|
||||
* deprotected restart page.
|
||||
*/
|
||||
err = ntfs_check_and_load_restart_page(log_na,
|
||||
(RESTART_PAGE_HEADER*)kaddr, pos,
|
||||
!rstr1_ph ? &rstr1_ph : &rstr2_ph,
|
||||
!rstr1_ph ? &rstr1_lsn : &rstr2_lsn);
|
||||
if (!err) {
|
||||
/*
|
||||
* If we have now found the first (modified by chkdsk)
|
||||
* restart page, continue looking for the second one.
|
||||
*/
|
||||
if (!pos) {
|
||||
pos = NTFS_BLOCK_SIZE >> 1;
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* We have now found the second (modified by chkdsk)
|
||||
* restart page, so we can stop looking.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Error output already done inside the function. Note, we do
|
||||
* not abort if the restart page was invalid as we might still
|
||||
* find a valid one further in the file.
|
||||
*/
|
||||
if (err != EINVAL)
|
||||
goto err_out;
|
||||
/* Continue looking. */
|
||||
if (!pos)
|
||||
pos = NTFS_BLOCK_SIZE >> 1;
|
||||
}
|
||||
if (kaddr) {
|
||||
free(kaddr);
|
||||
kaddr = NULL;
|
||||
}
|
||||
if (logfile_is_empty) {
|
||||
NVolSetLogFileEmpty(vol);
|
||||
is_empty:
|
||||
ntfs_log_trace("Done. ($LogFile is empty.)\n");
|
||||
return TRUE;
|
||||
}
|
||||
if (!rstr1_ph) {
|
||||
if (rstr2_ph)
|
||||
ntfs_log_error("BUG: rstr2_ph isn't NULL!\n");
|
||||
ntfs_log_error("Did not find any restart pages in "
|
||||
"$LogFile and it was not empty.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/* If both restart pages were found, use the more recent one. */
|
||||
if (rstr2_ph) {
|
||||
/*
|
||||
* If the second restart area is more recent, switch to it.
|
||||
* Otherwise just throw it away.
|
||||
*/
|
||||
if (rstr2_lsn > rstr1_lsn) {
|
||||
ntfs_log_debug("Using second restart page as it is more "
|
||||
"recent.\n");
|
||||
free(rstr1_ph);
|
||||
rstr1_ph = rstr2_ph;
|
||||
/* rstr1_lsn = rstr2_lsn; */
|
||||
} else {
|
||||
ntfs_log_debug("Using first restart page as it is more "
|
||||
"recent.\n");
|
||||
free(rstr2_ph);
|
||||
}
|
||||
rstr2_ph = NULL;
|
||||
}
|
||||
/* All consistency checks passed. */
|
||||
if (rp)
|
||||
*rp = rstr1_ph;
|
||||
else
|
||||
free(rstr1_ph);
|
||||
ntfs_log_trace("Done.\n");
|
||||
return TRUE;
|
||||
err_out:
|
||||
free(kaddr);
|
||||
free(rstr1_ph);
|
||||
free(rstr2_ph);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_is_logfile_clean - check in the journal if the volume is clean
|
||||
* @log_na: ntfs attribute of loaded journal $LogFile to check
|
||||
* @rp: copy of the current restart page
|
||||
*
|
||||
* Analyze the $LogFile journal and return TRUE if it indicates the volume was
|
||||
* shutdown cleanly and FALSE if not.
|
||||
*
|
||||
* At present we only look at the two restart pages and ignore the log record
|
||||
* pages. This is a little bit crude in that there will be a very small number
|
||||
* of cases where we think that a volume is dirty when in fact it is clean.
|
||||
* This should only affect volumes that have not been shutdown cleanly but did
|
||||
* not have any pending, non-check-pointed i/o, i.e. they were completely idle
|
||||
* at least for the five seconds preceding the unclean shutdown.
|
||||
*
|
||||
* This function assumes that the $LogFile journal has already been consistency
|
||||
* checked by a call to ntfs_check_logfile() and in particular if the $LogFile
|
||||
* is empty this function requires that NVolLogFileEmpty() is true otherwise an
|
||||
* empty volume will be reported as dirty.
|
||||
*/
|
||||
BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp)
|
||||
{
|
||||
RESTART_AREA *ra;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
/* An empty $LogFile must have been clean before it got emptied. */
|
||||
if (NVolLogFileEmpty(log_na->ni->vol)) {
|
||||
ntfs_log_trace("$LogFile is empty\n");
|
||||
return TRUE;
|
||||
}
|
||||
if (!rp) {
|
||||
ntfs_log_error("Restart page header is NULL\n");
|
||||
return FALSE;
|
||||
}
|
||||
if (!ntfs_is_rstr_record(rp->magic) &&
|
||||
!ntfs_is_chkd_record(rp->magic)) {
|
||||
ntfs_log_error("Restart page buffer is invalid\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
|
||||
/*
|
||||
* If the $LogFile has active clients, i.e. it is open, and we do not
|
||||
* have the RESTART_VOLUME_IS_CLEAN bit set in the restart area flags,
|
||||
* we assume there was an unclean shutdown.
|
||||
*/
|
||||
if (ra->client_in_use_list != LOGFILE_NO_CLIENT &&
|
||||
!(ra->flags & RESTART_VOLUME_IS_CLEAN)) {
|
||||
ntfs_log_error("The disk contains an unclean file system (%d, "
|
||||
"%d).\n", le16_to_cpu(ra->client_in_use_list),
|
||||
le16_to_cpu(ra->flags));
|
||||
return FALSE;
|
||||
}
|
||||
/* $LogFile indicates a clean shutdown. */
|
||||
ntfs_log_trace("$LogFile indicates a clean shutdown\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_empty_logfile - empty the contents of the $LogFile journal
|
||||
* @na: ntfs attribute of journal $LogFile to empty
|
||||
*
|
||||
* Empty the contents of the $LogFile journal @na and return 0 on success and
|
||||
* -1 on error.
|
||||
*
|
||||
* This function assumes that the $LogFile journal has already been consistency
|
||||
* checked by a call to ntfs_check_logfile() and that ntfs_is_logfile_clean()
|
||||
* has been used to ensure that the $LogFile is clean.
|
||||
*/
|
||||
int ntfs_empty_logfile(ntfs_attr *na)
|
||||
{
|
||||
s64 pos, count;
|
||||
char buf[NTFS_BUF_SIZE];
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
|
||||
if (NVolLogFileEmpty(na->ni->vol))
|
||||
return 0;
|
||||
|
||||
if (!NAttrNonResident(na)) {
|
||||
errno = EIO;
|
||||
ntfs_log_perror("Resident $LogFile $DATA attribute");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(buf, -1, NTFS_BUF_SIZE);
|
||||
|
||||
pos = 0;
|
||||
while ((count = na->data_size - pos) > 0) {
|
||||
|
||||
if (count > NTFS_BUF_SIZE)
|
||||
count = NTFS_BUF_SIZE;
|
||||
|
||||
count = ntfs_attr_pwrite(na, pos, count, buf);
|
||||
if (count <= 0) {
|
||||
ntfs_log_perror("Failed to reset $LogFile");
|
||||
if (count != -1)
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
pos += count;
|
||||
}
|
||||
|
||||
NVolSetLogFileEmpty(na->ni->vol);
|
||||
|
||||
return 0;
|
||||
}
|
394
source/logfile.h
Normal file
394
source/logfile.h
Normal file
@ -0,0 +1,394 @@
|
||||
/*
|
||||
* logfile.h - Exports for $LogFile handling. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2005 Anton Altaparmakov
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_LOGFILE_H
|
||||
#define _NTFS_LOGFILE_H
|
||||
|
||||
#include "types.h"
|
||||
#include "endians.h"
|
||||
#include "layout.h"
|
||||
|
||||
/*
|
||||
* Journal ($LogFile) organization:
|
||||
*
|
||||
* Two restart areas present in the first two pages (restart pages, one restart
|
||||
* area in each page). When the volume is dismounted they should be identical,
|
||||
* except for the update sequence array which usually has a different update
|
||||
* sequence number.
|
||||
*
|
||||
* These are followed by log records organized in pages headed by a log record
|
||||
* header going up to log file size. Not all pages contain log records when a
|
||||
* volume is first formatted, but as the volume ages, all records will be used.
|
||||
* When the log file fills up, the records at the beginning are purged (by
|
||||
* modifying the oldest_lsn to a higher value presumably) and writing begins
|
||||
* at the beginning of the file. Effectively, the log file is viewed as a
|
||||
* circular entity.
|
||||
*
|
||||
* NOTE: Windows NT, 2000, and XP all use log file version 1.1 but they accept
|
||||
* versions <= 1.x, including 0.-1. (Yes, that is a minus one in there!) We
|
||||
* probably only want to support 1.1 as this seems to be the current version
|
||||
* and we don't know how that differs from the older versions. The only
|
||||
* exception is if the journal is clean as marked by the two restart pages
|
||||
* then it doesn't matter whether we are on an earlier version. We can just
|
||||
* reinitialize the logfile and start again with version 1.1.
|
||||
*/
|
||||
|
||||
/* Some $LogFile related constants. */
|
||||
#define MaxLogFileSize 0x100000000ULL
|
||||
#define DefaultLogPageSize 4096
|
||||
#define MinLogRecordPages 48
|
||||
|
||||
/**
|
||||
* struct RESTART_PAGE_HEADER - Log file restart page header.
|
||||
*
|
||||
* Begins the restart area.
|
||||
*/
|
||||
typedef struct {
|
||||
/*Ofs*/
|
||||
/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */
|
||||
/* 0*/ NTFS_RECORD_TYPES magic;/* The magic is "RSTR". */
|
||||
/* 4*/ le16 usa_ofs; /* See NTFS_RECORD definition in layout.h.
|
||||
When creating, set this to be immediately
|
||||
after this header structure (without any
|
||||
alignment). */
|
||||
/* 6*/ le16 usa_count; /* See NTFS_RECORD definition in layout.h. */
|
||||
|
||||
/* 8*/ leLSN chkdsk_lsn; /* The last log file sequence number found by
|
||||
chkdsk. Only used when the magic is changed
|
||||
to "CHKD". Otherwise this is zero. */
|
||||
/* 16*/ le32 system_page_size; /* Byte size of system pages when the log file
|
||||
was created, has to be >= 512 and a power of
|
||||
2. Use this to calculate the required size
|
||||
of the usa (usa_count) and add it to usa_ofs.
|
||||
Then verify that the result is less than the
|
||||
value of the restart_area_offset. */
|
||||
/* 20*/ le32 log_page_size; /* Byte size of log file pages, has to be >=
|
||||
512 and a power of 2. The default is 4096
|
||||
and is used when the system page size is
|
||||
between 4096 and 8192. Otherwise this is
|
||||
set to the system page size instead. */
|
||||
/* 24*/ le16 restart_area_offset;/* Byte offset from the start of this header to
|
||||
the RESTART_AREA. Value has to be aligned
|
||||
to 8-byte boundary. When creating, set this
|
||||
to be after the usa. */
|
||||
/* 26*/ sle16 minor_ver; /* Log file minor version. Only check if major
|
||||
version is 1. */
|
||||
/* 28*/ sle16 major_ver; /* Log file major version. We only support
|
||||
version 1.1. */
|
||||
/* sizeof() = 30 (0x1e) bytes */
|
||||
} __attribute__((__packed__)) RESTART_PAGE_HEADER;
|
||||
|
||||
/*
|
||||
* Constant for the log client indices meaning that there are no client records
|
||||
* in this particular client array. Also inside the client records themselves,
|
||||
* this means that there are no client records preceding or following this one.
|
||||
*/
|
||||
#define LOGFILE_NO_CLIENT const_cpu_to_le16(0xffff)
|
||||
#define LOGFILE_NO_CLIENT_CPU 0xffff
|
||||
|
||||
/*
|
||||
* These are the so far known RESTART_AREA_* flags (16-bit) which contain
|
||||
* information about the log file in which they are present.
|
||||
*/
|
||||
enum {
|
||||
RESTART_VOLUME_IS_CLEAN = const_cpu_to_le16(0x0002),
|
||||
RESTART_SPACE_FILLER = 0xffff, /* gcc: Force enum bit width to 16. */
|
||||
} __attribute__((__packed__));
|
||||
|
||||
typedef le16 RESTART_AREA_FLAGS;
|
||||
|
||||
/**
|
||||
* struct RESTART_AREA - Log file restart area record.
|
||||
*
|
||||
* The offset of this record is found by adding the offset of the
|
||||
* RESTART_PAGE_HEADER to the restart_area_offset value found in it.
|
||||
* See notes at restart_area_offset above.
|
||||
*/
|
||||
typedef struct {
|
||||
/*Ofs*/
|
||||
/* 0*/ leLSN current_lsn; /* The current, i.e. last LSN inside the log
|
||||
when the restart area was last written.
|
||||
This happens often but what is the interval?
|
||||
Is it just fixed time or is it every time a
|
||||
check point is written or something else?
|
||||
On create set to 0. */
|
||||
/* 8*/ le16 log_clients; /* Number of log client records in the array of
|
||||
log client records which follows this
|
||||
restart area. Must be 1. */
|
||||
/* 10*/ le16 client_free_list; /* The index of the first free log client record
|
||||
in the array of log client records.
|
||||
LOGFILE_NO_CLIENT means that there are no
|
||||
free log client records in the array.
|
||||
If != LOGFILE_NO_CLIENT, check that
|
||||
log_clients > client_free_list. On Win2k
|
||||
and presumably earlier, on a clean volume
|
||||
this is != LOGFILE_NO_CLIENT, and it should
|
||||
be 0, i.e. the first (and only) client
|
||||
record is free and thus the logfile is
|
||||
closed and hence clean. A dirty volume
|
||||
would have left the logfile open and hence
|
||||
this would be LOGFILE_NO_CLIENT. On WinXP
|
||||
and presumably later, the logfile is always
|
||||
open, even on clean shutdown so this should
|
||||
always be LOGFILE_NO_CLIENT. */
|
||||
/* 12*/ le16 client_in_use_list;/* The index of the first in-use log client
|
||||
record in the array of log client records.
|
||||
LOGFILE_NO_CLIENT means that there are no
|
||||
in-use log client records in the array. If
|
||||
!= LOGFILE_NO_CLIENT check that log_clients
|
||||
> client_in_use_list. On Win2k and
|
||||
presumably earlier, on a clean volume this
|
||||
is LOGFILE_NO_CLIENT, i.e. there are no
|
||||
client records in use and thus the logfile
|
||||
is closed and hence clean. A dirty volume
|
||||
would have left the logfile open and hence
|
||||
this would be != LOGFILE_NO_CLIENT, and it
|
||||
should be 0, i.e. the first (and only)
|
||||
client record is in use. On WinXP and
|
||||
presumably later, the logfile is always
|
||||
open, even on clean shutdown so this should
|
||||
always be 0. */
|
||||
/* 14*/ RESTART_AREA_FLAGS flags;/* Flags modifying LFS behaviour. On Win2k
|
||||
and presumably earlier this is always 0. On
|
||||
WinXP and presumably later, if the logfile
|
||||
was shutdown cleanly, the second bit,
|
||||
RESTART_VOLUME_IS_CLEAN, is set. This bit
|
||||
is cleared when the volume is mounted by
|
||||
WinXP and set when the volume is dismounted,
|
||||
thus if the logfile is dirty, this bit is
|
||||
clear. Thus we don't need to check the
|
||||
Windows version to determine if the logfile
|
||||
is clean. Instead if the logfile is closed,
|
||||
we know it must be clean. If it is open and
|
||||
this bit is set, we also know it must be
|
||||
clean. If on the other hand the logfile is
|
||||
open and this bit is clear, we can be almost
|
||||
certain that the logfile is dirty. */
|
||||
/* 16*/ le32 seq_number_bits; /* How many bits to use for the sequence
|
||||
number. This is calculated as 67 - the
|
||||
number of bits required to store the logfile
|
||||
size in bytes and this can be used in with
|
||||
the specified file_size as a consistency
|
||||
check. */
|
||||
/* 20*/ le16 restart_area_length;/* Length of the restart area including the
|
||||
client array. Following checks required if
|
||||
version matches. Otherwise, skip them.
|
||||
restart_area_offset + restart_area_length
|
||||
has to be <= system_page_size. Also,
|
||||
restart_area_length has to be >=
|
||||
client_array_offset + (log_clients *
|
||||
sizeof(log client record)). */
|
||||
/* 22*/ le16 client_array_offset;/* Offset from the start of this record to
|
||||
the first log client record if versions are
|
||||
matched. When creating, set this to be
|
||||
after this restart area structure, aligned
|
||||
to 8-bytes boundary. If the versions do not
|
||||
match, this is ignored and the offset is
|
||||
assumed to be (sizeof(RESTART_AREA) + 7) &
|
||||
~7, i.e. rounded up to first 8-byte
|
||||
boundary. Either way, client_array_offset
|
||||
has to be aligned to an 8-byte boundary.
|
||||
Also, restart_area_offset +
|
||||
client_array_offset has to be <= 510.
|
||||
Finally, client_array_offset + (log_clients
|
||||
* sizeof(log client record)) has to be <=
|
||||
system_page_size. On Win2k and presumably
|
||||
earlier, this is 0x30, i.e. immediately
|
||||
following this record. On WinXP and
|
||||
presumably later, this is 0x40, i.e. there
|
||||
are 16 extra bytes between this record and
|
||||
the client array. This probably means that
|
||||
the RESTART_AREA record is actually bigger
|
||||
in WinXP and later. */
|
||||
/* 24*/ sle64 file_size; /* Usable byte size of the log file. If the
|
||||
restart_area_offset + the offset of the
|
||||
file_size are > 510 then corruption has
|
||||
occurred. This is the very first check when
|
||||
starting with the restart_area as if it
|
||||
fails it means that some of the above values
|
||||
will be corrupted by the multi sector
|
||||
transfer protection. The file_size has to
|
||||
be rounded down to be a multiple of the
|
||||
log_page_size in the RESTART_PAGE_HEADER and
|
||||
then it has to be at least big enough to
|
||||
store the two restart pages and 48 (0x30)
|
||||
log record pages. */
|
||||
/* 32*/ le32 last_lsn_data_length;/* Length of data of last LSN, not including
|
||||
the log record header. On create set to
|
||||
0. */
|
||||
/* 36*/ le16 log_record_header_length;/* Byte size of the log record header.
|
||||
If the version matches then check that the
|
||||
value of log_record_header_length is a
|
||||
multiple of 8, i.e.
|
||||
(log_record_header_length + 7) & ~7 ==
|
||||
log_record_header_length. When creating set
|
||||
it to sizeof(LOG_RECORD_HEADER), aligned to
|
||||
8 bytes. */
|
||||
/* 38*/ le16 log_page_data_offset;/* Offset to the start of data in a log record
|
||||
page. Must be a multiple of 8. On create
|
||||
set it to immediately after the update
|
||||
sequence array of the log record page. */
|
||||
/* 40*/ le32 restart_log_open_count;/* A counter that gets incremented every
|
||||
time the logfile is restarted which happens
|
||||
at mount time when the logfile is opened.
|
||||
When creating set to a random value. Win2k
|
||||
sets it to the low 32 bits of the current
|
||||
system time in NTFS format (see time.h). */
|
||||
/* 44*/ le32 reserved; /* Reserved/alignment to 8-byte boundary. */
|
||||
/* sizeof() = 48 (0x30) bytes */
|
||||
} __attribute__((__packed__)) RESTART_AREA;
|
||||
|
||||
/**
|
||||
* struct LOG_CLIENT_RECORD - Log client record.
|
||||
*
|
||||
* The offset of this record is found by adding the offset of the
|
||||
* RESTART_AREA to the client_array_offset value found in it.
|
||||
*/
|
||||
typedef struct {
|
||||
/*Ofs*/
|
||||
/* 0*/ leLSN oldest_lsn; /* Oldest LSN needed by this client. On create
|
||||
set to 0. */
|
||||
/* 8*/ leLSN client_restart_lsn;/* LSN at which this client needs to restart
|
||||
the volume, i.e. the current position within
|
||||
the log file. At present, if clean this
|
||||
should = current_lsn in restart area but it
|
||||
probably also = current_lsn when dirty most
|
||||
of the time. At create set to 0. */
|
||||
/* 16*/ le16 prev_client; /* The offset to the previous log client record
|
||||
in the array of log client records.
|
||||
LOGFILE_NO_CLIENT means there is no previous
|
||||
client record, i.e. this is the first one.
|
||||
This is always LOGFILE_NO_CLIENT. */
|
||||
/* 18*/ le16 next_client; /* The offset to the next log client record in
|
||||
the array of log client records.
|
||||
LOGFILE_NO_CLIENT means there are no next
|
||||
client records, i.e. this is the last one.
|
||||
This is always LOGFILE_NO_CLIENT. */
|
||||
/* 20*/ le16 seq_number; /* On Win2k and presumably earlier, this is set
|
||||
to zero every time the logfile is restarted
|
||||
and it is incremented when the logfile is
|
||||
closed at dismount time. Thus it is 0 when
|
||||
dirty and 1 when clean. On WinXP and
|
||||
presumably later, this is always 0. */
|
||||
/* 22*/ u8 reserved[6]; /* Reserved/alignment. */
|
||||
/* 28*/ le32 client_name_length;/* Length of client name in bytes. Should
|
||||
always be 8. */
|
||||
/* 32*/ ntfschar client_name[64];/* Name of the client in Unicode. Should
|
||||
always be "NTFS" with the remaining bytes
|
||||
set to 0. */
|
||||
/* sizeof() = 160 (0xa0) bytes */
|
||||
} __attribute__((__packed__)) LOG_CLIENT_RECORD;
|
||||
|
||||
/**
|
||||
* struct RECORD_PAGE_HEADER - Log page record page header.
|
||||
*
|
||||
* Each log page begins with this header and is followed by several LOG_RECORD
|
||||
* structures, starting at offset 0x40 (the size of this structure and the
|
||||
* following update sequence array and then aligned to 8 byte boundary, but is
|
||||
* this specified anywhere?).
|
||||
*/
|
||||
typedef struct {
|
||||
/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */
|
||||
NTFS_RECORD_TYPES magic;/* Usually the magic is "RCRD". */
|
||||
u16 usa_ofs; /* See NTFS_RECORD definition in layout.h.
|
||||
When creating, set this to be immediately
|
||||
after this header structure (without any
|
||||
alignment). */
|
||||
u16 usa_count; /* See NTFS_RECORD definition in layout.h. */
|
||||
|
||||
union {
|
||||
LSN last_lsn;
|
||||
s64 file_offset;
|
||||
} __attribute__((__packed__)) copy;
|
||||
u32 flags;
|
||||
u16 page_count;
|
||||
u16 page_position;
|
||||
union {
|
||||
struct {
|
||||
u16 next_record_offset;
|
||||
u8 reserved[6];
|
||||
LSN last_end_lsn;
|
||||
} __attribute__((__packed__)) packed;
|
||||
} __attribute__((__packed__)) header;
|
||||
} __attribute__((__packed__)) RECORD_PAGE_HEADER;
|
||||
|
||||
/**
|
||||
* enum LOG_RECORD_FLAGS - Possible 16-bit flags for log records.
|
||||
*
|
||||
* (Or is it log record pages?)
|
||||
*/
|
||||
typedef enum {
|
||||
LOG_RECORD_MULTI_PAGE = const_cpu_to_le16(0x0001), /* ??? */
|
||||
LOG_RECORD_SIZE_PLACE_HOLDER = 0xffff,
|
||||
/* This has nothing to do with the log record. It is only so
|
||||
gcc knows to make the flags 16-bit. */
|
||||
} __attribute__((__packed__)) LOG_RECORD_FLAGS;
|
||||
|
||||
/**
|
||||
* struct LOG_CLIENT_ID - The log client id structure identifying a log client.
|
||||
*/
|
||||
typedef struct {
|
||||
u16 seq_number;
|
||||
u16 client_index;
|
||||
} __attribute__((__packed__)) LOG_CLIENT_ID;
|
||||
|
||||
/**
|
||||
* struct LOG_RECORD - Log record header.
|
||||
*
|
||||
* Each log record seems to have a constant size of 0x70 bytes.
|
||||
*/
|
||||
typedef struct {
|
||||
LSN this_lsn;
|
||||
LSN client_previous_lsn;
|
||||
LSN client_undo_next_lsn;
|
||||
u32 client_data_length;
|
||||
LOG_CLIENT_ID client_id;
|
||||
u32 record_type;
|
||||
u32 transaction_id;
|
||||
u16 flags;
|
||||
u16 reserved_or_alignment[3];
|
||||
/* Now are at ofs 0x30 into struct. */
|
||||
u16 redo_operation;
|
||||
u16 undo_operation;
|
||||
u16 redo_offset;
|
||||
u16 redo_length;
|
||||
u16 undo_offset;
|
||||
u16 undo_length;
|
||||
u16 target_attribute;
|
||||
u16 lcns_to_follow; /* Number of lcn_list entries
|
||||
following this entry. */
|
||||
/* Now at ofs 0x40. */
|
||||
u16 record_offset;
|
||||
u16 attribute_offset;
|
||||
u32 alignment_or_reserved;
|
||||
VCN target_vcn;
|
||||
/* Now at ofs 0x50. */
|
||||
struct { /* Only present if lcns_to_follow
|
||||
is not 0. */
|
||||
LCN lcn;
|
||||
} __attribute__((__packed__)) lcn_list[0];
|
||||
} __attribute__((__packed__)) LOG_RECORD;
|
||||
|
||||
extern BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp);
|
||||
extern BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp);
|
||||
extern int ntfs_empty_logfile(ntfs_attr *na);
|
||||
|
||||
#endif /* defined _NTFS_LOGFILE_H */
|
613
source/logging.c
Normal file
613
source/logging.c
Normal file
@ -0,0 +1,613 @@
|
||||
/**
|
||||
* logging.c - Centralised logging. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2005 Richard Russon
|
||||
* Copyright (c) 2005-2008 Szabolcs Szakacsits
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDARG_H
|
||||
#include <stdarg.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYSLOG_H
|
||||
#include <syslog.h>
|
||||
#endif
|
||||
|
||||
#include "logging.h"
|
||||
#include "misc.h"
|
||||
|
||||
#ifndef PATH_SEP
|
||||
#define PATH_SEP '/'
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
static int tab;
|
||||
#endif
|
||||
|
||||
/* Some gcc 3.x, 4.[01].X crash with internal compiler error. */
|
||||
#if __GNUC__ <= 3 || (__GNUC__ == 4 && __GNUC_MINOR__ <= 1)
|
||||
# define BROKEN_GCC_FORMAT_ATTRIBUTE
|
||||
#else
|
||||
# define BROKEN_GCC_FORMAT_ATTRIBUTE __attribute__((format(printf, 6, 0)))
|
||||
#endif
|
||||
|
||||
/**
|
||||
* struct ntfs_logging - Control info for the logging system
|
||||
* @levels: Bitfield of logging levels
|
||||
* @flags: Flags which affect the output style
|
||||
* @handler: Function to perform the actual logging
|
||||
*/
|
||||
struct ntfs_logging {
|
||||
u32 levels;
|
||||
u32 flags;
|
||||
ntfs_log_handler *handler BROKEN_GCC_FORMAT_ATTRIBUTE;
|
||||
};
|
||||
|
||||
/**
|
||||
* ntfs_log
|
||||
* This struct controls all the logging within the library and tools.
|
||||
*/
|
||||
static struct ntfs_logging ntfs_log = {
|
||||
#ifdef DEBUG
|
||||
NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE | NTFS_LOG_LEVEL_ENTER |
|
||||
NTFS_LOG_LEVEL_LEAVE |
|
||||
#endif
|
||||
NTFS_LOG_LEVEL_INFO | NTFS_LOG_LEVEL_QUIET | NTFS_LOG_LEVEL_WARNING |
|
||||
NTFS_LOG_LEVEL_ERROR | NTFS_LOG_LEVEL_PERROR | NTFS_LOG_LEVEL_CRITICAL |
|
||||
NTFS_LOG_LEVEL_PROGRESS,
|
||||
NTFS_LOG_FLAG_ONLYNAME,
|
||||
#ifdef DEBUG
|
||||
ntfs_log_handler_outerr
|
||||
#else
|
||||
ntfs_log_handler_null
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* ntfs_log_get_levels - Get a list of the current logging levels
|
||||
*
|
||||
* Find out which logging levels are enabled.
|
||||
*
|
||||
* Returns: Log levels in a 32-bit field
|
||||
*/
|
||||
u32 ntfs_log_get_levels(void)
|
||||
{
|
||||
return ntfs_log.levels;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_set_levels - Enable extra logging levels
|
||||
* @levels: 32-bit field of log levels to set
|
||||
*
|
||||
* Enable one or more logging levels.
|
||||
* The logging levels are named: NTFS_LOG_LEVEL_*.
|
||||
*
|
||||
* Returns: Log levels that were enabled before the call
|
||||
*/
|
||||
u32 ntfs_log_set_levels(u32 levels)
|
||||
{
|
||||
u32 old;
|
||||
old = ntfs_log.levels;
|
||||
ntfs_log.levels |= levels;
|
||||
return old;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_clear_levels - Disable some logging levels
|
||||
* @levels: 32-bit field of log levels to clear
|
||||
*
|
||||
* Disable one or more logging levels.
|
||||
* The logging levels are named: NTFS_LOG_LEVEL_*.
|
||||
*
|
||||
* Returns: Log levels that were enabled before the call
|
||||
*/
|
||||
u32 ntfs_log_clear_levels(u32 levels)
|
||||
{
|
||||
u32 old;
|
||||
old = ntfs_log.levels;
|
||||
ntfs_log.levels &= (~levels);
|
||||
return old;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ntfs_log_get_flags - Get a list of logging style flags
|
||||
*
|
||||
* Find out which logging flags are enabled.
|
||||
*
|
||||
* Returns: Logging flags in a 32-bit field
|
||||
*/
|
||||
u32 ntfs_log_get_flags(void)
|
||||
{
|
||||
return ntfs_log.flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_set_flags - Enable extra logging style flags
|
||||
* @flags: 32-bit field of logging flags to set
|
||||
*
|
||||
* Enable one or more logging flags.
|
||||
* The log flags are named: NTFS_LOG_LEVEL_*.
|
||||
*
|
||||
* Returns: Logging flags that were enabled before the call
|
||||
*/
|
||||
u32 ntfs_log_set_flags(u32 flags)
|
||||
{
|
||||
u32 old;
|
||||
old = ntfs_log.flags;
|
||||
ntfs_log.flags |= flags;
|
||||
return old;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_clear_flags - Disable some logging styles
|
||||
* @flags: 32-bit field of logging flags to clear
|
||||
*
|
||||
* Disable one or more logging flags.
|
||||
* The log flags are named: NTFS_LOG_LEVEL_*.
|
||||
*
|
||||
* Returns: Logging flags that were enabled before the call
|
||||
*/
|
||||
u32 ntfs_log_clear_flags(u32 flags)
|
||||
{
|
||||
u32 old;
|
||||
old = ntfs_log.flags;
|
||||
ntfs_log.flags &= (~flags);
|
||||
return old;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ntfs_log_get_stream - Default output streams for logging levels
|
||||
* @level: Log level
|
||||
*
|
||||
* By default, urgent messages are sent to "stderr".
|
||||
* Other messages are sent to "stdout".
|
||||
*
|
||||
* Returns: "string" Prefix to be used
|
||||
*/
|
||||
static FILE * ntfs_log_get_stream(u32 level)
|
||||
{
|
||||
FILE *stream;
|
||||
|
||||
switch (level) {
|
||||
case NTFS_LOG_LEVEL_INFO:
|
||||
case NTFS_LOG_LEVEL_QUIET:
|
||||
case NTFS_LOG_LEVEL_PROGRESS:
|
||||
case NTFS_LOG_LEVEL_VERBOSE:
|
||||
stream = stdout;
|
||||
break;
|
||||
|
||||
case NTFS_LOG_LEVEL_DEBUG:
|
||||
case NTFS_LOG_LEVEL_TRACE:
|
||||
case NTFS_LOG_LEVEL_ENTER:
|
||||
case NTFS_LOG_LEVEL_LEAVE:
|
||||
case NTFS_LOG_LEVEL_WARNING:
|
||||
case NTFS_LOG_LEVEL_ERROR:
|
||||
case NTFS_LOG_LEVEL_CRITICAL:
|
||||
case NTFS_LOG_LEVEL_PERROR:
|
||||
default:
|
||||
stream = stderr;
|
||||
break;
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_get_prefix - Default prefixes for logging levels
|
||||
* @level: Log level to be prefixed
|
||||
*
|
||||
* Prefixing the logging output can make it easier to parse.
|
||||
*
|
||||
* Returns: "string" Prefix to be used
|
||||
*/
|
||||
static const char * ntfs_log_get_prefix(u32 level)
|
||||
{
|
||||
const char *prefix;
|
||||
|
||||
switch (level) {
|
||||
case NTFS_LOG_LEVEL_DEBUG:
|
||||
prefix = "DEBUG: ";
|
||||
break;
|
||||
case NTFS_LOG_LEVEL_TRACE:
|
||||
prefix = "TRACE: ";
|
||||
break;
|
||||
case NTFS_LOG_LEVEL_QUIET:
|
||||
prefix = "QUIET: ";
|
||||
break;
|
||||
case NTFS_LOG_LEVEL_INFO:
|
||||
prefix = "INFO: ";
|
||||
break;
|
||||
case NTFS_LOG_LEVEL_VERBOSE:
|
||||
prefix = "VERBOSE: ";
|
||||
break;
|
||||
case NTFS_LOG_LEVEL_PROGRESS:
|
||||
prefix = "PROGRESS: ";
|
||||
break;
|
||||
case NTFS_LOG_LEVEL_WARNING:
|
||||
prefix = "WARNING: ";
|
||||
break;
|
||||
case NTFS_LOG_LEVEL_ERROR:
|
||||
prefix = "ERROR: ";
|
||||
break;
|
||||
case NTFS_LOG_LEVEL_PERROR:
|
||||
prefix = "ERROR: ";
|
||||
break;
|
||||
case NTFS_LOG_LEVEL_CRITICAL:
|
||||
prefix = "CRITICAL: ";
|
||||
break;
|
||||
default:
|
||||
prefix = "";
|
||||
break;
|
||||
}
|
||||
|
||||
return prefix;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ntfs_log_set_handler - Provide an alternate logging handler
|
||||
* @handler: function to perform the logging
|
||||
*
|
||||
* This alternate handler will be called for all future logging requests.
|
||||
* If no @handler is specified, logging will revert to the default handler.
|
||||
*/
|
||||
void ntfs_log_set_handler(ntfs_log_handler *handler)
|
||||
{
|
||||
if (handler) {
|
||||
ntfs_log.handler = handler;
|
||||
#ifdef HAVE_SYSLOG_H
|
||||
if (handler == ntfs_log_handler_syslog)
|
||||
openlog("ntfs-3g", LOG_PID, LOG_USER);
|
||||
#endif
|
||||
} else
|
||||
ntfs_log.handler = ntfs_log_handler_null;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_redirect - Pass on the request to the real handler
|
||||
* @function: Function in which the log line occurred
|
||||
* @file: File in which the log line occurred
|
||||
* @line: Line number on which the log line occurred
|
||||
* @level: Level at which the line is logged
|
||||
* @data: User specified data, possibly specific to a handler
|
||||
* @format: printf-style formatting string
|
||||
* @...: Arguments to be formatted
|
||||
*
|
||||
* This is just a redirector function. The arguments are simply passed to the
|
||||
* main logging handler (as defined in the global logging struct @ntfs_log).
|
||||
*
|
||||
* Returns: -1 Error occurred
|
||||
* 0 Message wasn't logged
|
||||
* num Number of output characters
|
||||
*/
|
||||
int ntfs_log_redirect(const char *function, const char *file,
|
||||
int line, u32 level, void *data, const char *format, ...)
|
||||
{
|
||||
int olderr = errno;
|
||||
int ret;
|
||||
va_list args;
|
||||
|
||||
if (!(ntfs_log.levels & level)) /* Don't log this message */
|
||||
return 0;
|
||||
|
||||
va_start(args, format);
|
||||
errno = olderr;
|
||||
ret = ntfs_log.handler(function, file, line, level, data, format, args);
|
||||
va_end(args);
|
||||
|
||||
errno = olderr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ntfs_log_handler_syslog - syslog logging handler
|
||||
* @function: Function in which the log line occurred
|
||||
* @file: File in which the log line occurred
|
||||
* @line: Line number on which the log line occurred
|
||||
* @level: Level at which the line is logged
|
||||
* @data: User specified data, possibly specific to a handler
|
||||
* @format: printf-style formatting string
|
||||
* @args: Arguments to be formatted
|
||||
*
|
||||
* A simple syslog logging handler. Ignores colors.
|
||||
*
|
||||
* Returns: -1 Error occurred
|
||||
* 0 Message wasn't logged
|
||||
* num Number of output characters
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_SYSLOG_H
|
||||
|
||||
#define LOG_LINE_LEN 512
|
||||
|
||||
int ntfs_log_handler_syslog(const char *function __attribute__((unused)),
|
||||
const char *file __attribute__((unused)),
|
||||
int line __attribute__((unused)), u32 level,
|
||||
void *data __attribute__((unused)),
|
||||
const char *format, va_list args)
|
||||
{
|
||||
char logbuf[LOG_LINE_LEN];
|
||||
int ret, olderr = errno;
|
||||
|
||||
#ifndef DEBUG
|
||||
if ((level & NTFS_LOG_LEVEL_PERROR) && errno == ENOSPC)
|
||||
return 1;
|
||||
#endif
|
||||
ret = vsnprintf(logbuf, LOG_LINE_LEN, format, args);
|
||||
if (ret < 0) {
|
||||
vsyslog(LOG_NOTICE, format, args);
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((LOG_LINE_LEN > ret + 3) && (level & NTFS_LOG_LEVEL_PERROR)) {
|
||||
strncat(logbuf, ": ", LOG_LINE_LEN - ret - 1);
|
||||
strncat(logbuf, strerror(olderr), LOG_LINE_LEN - (ret + 3));
|
||||
ret = strlen(logbuf);
|
||||
}
|
||||
|
||||
syslog(LOG_NOTICE, "%s", logbuf);
|
||||
out:
|
||||
errno = olderr;
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ntfs_log_handler_fprintf - Basic logging handler
|
||||
* @function: Function in which the log line occurred
|
||||
* @file: File in which the log line occurred
|
||||
* @line: Line number on which the log line occurred
|
||||
* @level: Level at which the line is logged
|
||||
* @data: User specified data, possibly specific to a handler
|
||||
* @format: printf-style formatting string
|
||||
* @args: Arguments to be formatted
|
||||
*
|
||||
* A simple logging handler. This is where the log line is finally displayed.
|
||||
* It is more likely that you will want to set the handler to either
|
||||
* ntfs_log_handler_outerr or ntfs_log_handler_stderr.
|
||||
*
|
||||
* Note: For this handler, @data is a pointer to a FILE output stream.
|
||||
* If @data is NULL, nothing will be displayed.
|
||||
*
|
||||
* Returns: -1 Error occurred
|
||||
* 0 Message wasn't logged
|
||||
* num Number of output characters
|
||||
*/
|
||||
int ntfs_log_handler_fprintf(const char *function, const char *file,
|
||||
int line, u32 level, void *data, const char *format, va_list args)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
int i;
|
||||
#endif
|
||||
int ret = 0;
|
||||
int olderr = errno;
|
||||
FILE *stream;
|
||||
|
||||
if (!data) /* Interpret data as a FILE stream. */
|
||||
return 0; /* If it's NULL, we can't do anything. */
|
||||
stream = (FILE*)data;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (level == NTFS_LOG_LEVEL_LEAVE) {
|
||||
if (tab)
|
||||
tab--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < tab; i++)
|
||||
ret += fprintf(stream, " ");
|
||||
#endif
|
||||
if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) &&
|
||||
(strchr(file, PATH_SEP))) /* Abbreviate the filename */
|
||||
file = strrchr(file, PATH_SEP) + 1;
|
||||
|
||||
if (ntfs_log.flags & NTFS_LOG_FLAG_PREFIX) /* Prefix the output */
|
||||
ret += fprintf(stream, "%s", ntfs_log_get_prefix(level));
|
||||
|
||||
if (ntfs_log.flags & NTFS_LOG_FLAG_FILENAME) /* Source filename */
|
||||
ret += fprintf(stream, "%s ", file);
|
||||
|
||||
if (ntfs_log.flags & NTFS_LOG_FLAG_LINE) /* Source line number */
|
||||
ret += fprintf(stream, "(%d) ", line);
|
||||
|
||||
if ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION) || /* Source function */
|
||||
(level & NTFS_LOG_LEVEL_TRACE) || (level & NTFS_LOG_LEVEL_ENTER))
|
||||
ret += fprintf(stream, "%s(): ", function);
|
||||
|
||||
ret += vfprintf(stream, format, args);
|
||||
|
||||
if (level & NTFS_LOG_LEVEL_PERROR)
|
||||
ret += fprintf(stream, ": %s\n", strerror(olderr));
|
||||
|
||||
#ifdef DEBUG
|
||||
if (level == NTFS_LOG_LEVEL_ENTER)
|
||||
tab++;
|
||||
#endif
|
||||
fflush(stream);
|
||||
errno = olderr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_handler_null - Null logging handler (no output)
|
||||
* @function: Function in which the log line occurred
|
||||
* @file: File in which the log line occurred
|
||||
* @line: Line number on which the log line occurred
|
||||
* @level: Level at which the line is logged
|
||||
* @data: User specified data, possibly specific to a handler
|
||||
* @format: printf-style formatting string
|
||||
* @args: Arguments to be formatted
|
||||
*
|
||||
* This handler produces no output. It provides a way to temporarily disable
|
||||
* logging, without having to change the levels and flags.
|
||||
*
|
||||
* Returns: 0 Message wasn't logged
|
||||
*/
|
||||
int ntfs_log_handler_null(const char *function __attribute__((unused)), const char *file __attribute__((unused)),
|
||||
int line __attribute__((unused)), u32 level __attribute__((unused)), void *data __attribute__((unused)),
|
||||
const char *format __attribute__((unused)), va_list args __attribute__((unused)))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_handler_stdout - All logs go to stdout
|
||||
* @function: Function in which the log line occurred
|
||||
* @file: File in which the log line occurred
|
||||
* @line: Line number on which the log line occurred
|
||||
* @level: Level at which the line is logged
|
||||
* @data: User specified data, possibly specific to a handler
|
||||
* @format: printf-style formatting string
|
||||
* @args: Arguments to be formatted
|
||||
*
|
||||
* Display a log message to stdout.
|
||||
*
|
||||
* Note: For this handler, @data is a pointer to a FILE output stream.
|
||||
* If @data is NULL, then stdout will be used.
|
||||
*
|
||||
* Note: This function calls ntfs_log_handler_fprintf to do the main work.
|
||||
*
|
||||
* Returns: -1 Error occurred
|
||||
* 0 Message wasn't logged
|
||||
* num Number of output characters
|
||||
*/
|
||||
int ntfs_log_handler_stdout(const char *function, const char *file,
|
||||
int line, u32 level, void *data, const char *format, va_list args)
|
||||
{
|
||||
if (!data)
|
||||
data = stdout;
|
||||
|
||||
return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_handler_outerr - Logs go to stdout/stderr depending on level
|
||||
* @function: Function in which the log line occurred
|
||||
* @file: File in which the log line occurred
|
||||
* @line: Line number on which the log line occurred
|
||||
* @level: Level at which the line is logged
|
||||
* @data: User specified data, possibly specific to a handler
|
||||
* @format: printf-style formatting string
|
||||
* @args: Arguments to be formatted
|
||||
*
|
||||
* Display a log message. The output stream will be determined by the log
|
||||
* level.
|
||||
*
|
||||
* Note: For this handler, @data is a pointer to a FILE output stream.
|
||||
* If @data is NULL, the function ntfs_log_get_stream will be called
|
||||
*
|
||||
* Note: This function calls ntfs_log_handler_fprintf to do the main work.
|
||||
*
|
||||
* Returns: -1 Error occurred
|
||||
* 0 Message wasn't logged
|
||||
* num Number of output characters
|
||||
*/
|
||||
int ntfs_log_handler_outerr(const char *function, const char *file,
|
||||
int line, u32 level, void *data, const char *format, va_list args)
|
||||
{
|
||||
if (!data)
|
||||
data = ntfs_log_get_stream(level);
|
||||
|
||||
return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_handler_stderr - All logs go to stderr
|
||||
* @function: Function in which the log line occurred
|
||||
* @file: File in which the log line occurred
|
||||
* @line: Line number on which the log line occurred
|
||||
* @level: Level at which the line is logged
|
||||
* @data: User specified data, possibly specific to a handler
|
||||
* @format: printf-style formatting string
|
||||
* @args: Arguments to be formatted
|
||||
*
|
||||
* Display a log message to stderr.
|
||||
*
|
||||
* Note: For this handler, @data is a pointer to a FILE output stream.
|
||||
* If @data is NULL, then stdout will be used.
|
||||
*
|
||||
* Note: This function calls ntfs_log_handler_fprintf to do the main work.
|
||||
*
|
||||
* Returns: -1 Error occurred
|
||||
* 0 Message wasn't logged
|
||||
* num Number of output characters
|
||||
*/
|
||||
int ntfs_log_handler_stderr(const char *function, const char *file,
|
||||
int line, u32 level, void *data, const char *format, va_list args)
|
||||
{
|
||||
if (!data)
|
||||
data = stderr;
|
||||
|
||||
return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ntfs_log_parse_option - Act upon command line options
|
||||
* @option: Option flag
|
||||
*
|
||||
* Delegate some of the work of parsing the command line. All the options begin
|
||||
* with "--log-". Options cause log levels to be enabled in @ntfs_log (the
|
||||
* global logging structure).
|
||||
*
|
||||
* Note: The "colour" option changes the logging handler.
|
||||
*
|
||||
* Returns: TRUE Option understood
|
||||
* FALSE Invalid log option
|
||||
*/
|
||||
BOOL ntfs_log_parse_option(const char *option)
|
||||
{
|
||||
if (strcmp(option, "--log-debug") == 0) {
|
||||
ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG);
|
||||
return TRUE;
|
||||
} else if (strcmp(option, "--log-verbose") == 0) {
|
||||
ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE);
|
||||
return TRUE;
|
||||
} else if (strcmp(option, "--log-quiet") == 0) {
|
||||
ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET);
|
||||
return TRUE;
|
||||
} else if (strcmp(option, "--log-trace") == 0) {
|
||||
ntfs_log_set_levels(NTFS_LOG_LEVEL_TRACE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
ntfs_log_debug("Unknown logging option '%s'\n", option);
|
||||
return FALSE;
|
||||
}
|
||||
|
118
source/logging.h
Normal file
118
source/logging.h
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* logging.h - Centralised logging. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2005 Richard Russon
|
||||
* Copyright (c) 2007-2008 Szabolcs Szakacsits
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _LOGGING_H_
|
||||
#define _LOGGING_H_
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDARG_H
|
||||
#include <stdarg.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/* Function prototype for the logging handlers */
|
||||
typedef int (ntfs_log_handler)(const char *function, const char *file, int line,
|
||||
u32 level, void *data, const char *format, va_list args);
|
||||
|
||||
/* Set the logging handler from one of the functions, below. */
|
||||
void ntfs_log_set_handler(ntfs_log_handler *handler
|
||||
__attribute__((format(printf, 6, 0))));
|
||||
|
||||
/* Logging handlers */
|
||||
ntfs_log_handler ntfs_log_handler_syslog __attribute__((format(printf, 6, 0)));
|
||||
ntfs_log_handler ntfs_log_handler_fprintf __attribute__((format(printf, 6, 0)));
|
||||
ntfs_log_handler ntfs_log_handler_null __attribute__((format(printf, 6, 0)));
|
||||
ntfs_log_handler ntfs_log_handler_stdout __attribute__((format(printf, 6, 0)));
|
||||
ntfs_log_handler ntfs_log_handler_outerr __attribute__((format(printf, 6, 0)));
|
||||
ntfs_log_handler ntfs_log_handler_stderr __attribute__((format(printf, 6, 0)));
|
||||
|
||||
/* Enable/disable certain log levels */
|
||||
u32 ntfs_log_set_levels(u32 levels);
|
||||
u32 ntfs_log_clear_levels(u32 levels);
|
||||
u32 ntfs_log_get_levels(void);
|
||||
|
||||
/* Enable/disable certain log flags */
|
||||
u32 ntfs_log_set_flags(u32 flags);
|
||||
u32 ntfs_log_clear_flags(u32 flags);
|
||||
u32 ntfs_log_get_flags(void);
|
||||
|
||||
/* Turn command-line options into logging flags */
|
||||
BOOL ntfs_log_parse_option(const char *option);
|
||||
|
||||
int ntfs_log_redirect(const char *function, const char *file, int line,
|
||||
u32 level, void *data, const char *format, ...)
|
||||
__attribute__((format(printf, 6, 7)));
|
||||
|
||||
/* Logging levels - Determine what gets logged */
|
||||
#define NTFS_LOG_LEVEL_DEBUG (1 << 0) /* x = 42 */
|
||||
#define NTFS_LOG_LEVEL_TRACE (1 << 1) /* Entering function x() */
|
||||
#define NTFS_LOG_LEVEL_QUIET (1 << 2) /* Quietable output */
|
||||
#define NTFS_LOG_LEVEL_INFO (1 << 3) /* Volume needs defragmenting */
|
||||
#define NTFS_LOG_LEVEL_VERBOSE (1 << 4) /* Forced to continue */
|
||||
#define NTFS_LOG_LEVEL_PROGRESS (1 << 5) /* 54% complete */
|
||||
#define NTFS_LOG_LEVEL_WARNING (1 << 6) /* You should backup before starting */
|
||||
#define NTFS_LOG_LEVEL_ERROR (1 << 7) /* Operation failed, no damage done */
|
||||
#define NTFS_LOG_LEVEL_PERROR (1 << 8) /* Message : standard error description */
|
||||
#define NTFS_LOG_LEVEL_CRITICAL (1 << 9) /* Operation failed,damage may have occurred */
|
||||
#define NTFS_LOG_LEVEL_ENTER (1 << 10) /* Enter a function */
|
||||
#define NTFS_LOG_LEVEL_LEAVE (1 << 11) /* Leave a function */
|
||||
|
||||
/* Logging style flags - Manage the style of the output */
|
||||
#define NTFS_LOG_FLAG_PREFIX (1 << 0) /* Prefix messages with "ERROR: ", etc */
|
||||
#define NTFS_LOG_FLAG_FILENAME (1 << 1) /* Show the file origin of the message */
|
||||
#define NTFS_LOG_FLAG_LINE (1 << 2) /* Show the line number of the message */
|
||||
#define NTFS_LOG_FLAG_FUNCTION (1 << 3) /* Show the function name containing the message */
|
||||
#define NTFS_LOG_FLAG_ONLYNAME (1 << 4) /* Only display the filename, not the pathname */
|
||||
|
||||
/* Macros to simplify logging. One for each level defined above.
|
||||
* Note, ntfs_log_debug/trace have effect only if DEBUG is defined.
|
||||
*/
|
||||
#define ntfs_log_critical(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_CRITICAL,NULL,FORMAT,##ARGS)
|
||||
#define ntfs_log_error(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_ERROR,NULL,FORMAT,##ARGS)
|
||||
#define ntfs_log_info(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_INFO,NULL,FORMAT,##ARGS)
|
||||
#define ntfs_log_perror(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_PERROR,NULL,FORMAT,##ARGS)
|
||||
#define ntfs_log_progress(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_PROGRESS,NULL,FORMAT,##ARGS)
|
||||
#define ntfs_log_quiet(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_QUIET,NULL,FORMAT,##ARGS)
|
||||
#define ntfs_log_verbose(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_VERBOSE,NULL,FORMAT,##ARGS)
|
||||
#define ntfs_log_warning(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_WARNING,NULL,FORMAT,##ARGS)
|
||||
|
||||
/* By default debug and trace messages are compiled into the program,
|
||||
* but not displayed.
|
||||
*/
|
||||
#ifdef DEBUG
|
||||
#define ntfs_log_debug(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_DEBUG,NULL,FORMAT,##ARGS)
|
||||
#define ntfs_log_trace(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_TRACE,NULL,FORMAT,##ARGS)
|
||||
#define ntfs_log_enter(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_ENTER,NULL,FORMAT,##ARGS)
|
||||
#define ntfs_log_leave(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_LEAVE,NULL,FORMAT,##ARGS)
|
||||
#else
|
||||
#define ntfs_log_debug(FORMAT, ARGS...)do {} while (0)
|
||||
#define ntfs_log_trace(FORMAT, ARGS...)do {} while (0)
|
||||
#define ntfs_log_enter(FORMAT, ARGS...)do {} while (0)
|
||||
#define ntfs_log_leave(FORMAT, ARGS...)do {} while (0)
|
||||
#endif /* DEBUG */
|
||||
|
||||
#endif /* _LOGGING_H_ */
|
||||
|
43
source/mem_allocate.h
Normal file
43
source/mem_allocate.h
Normal file
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* mem_allocate.h - Memory allocation and destruction calls.
|
||||
*
|
||||
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
||||
* Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _MEM_ALLOCATE_H
|
||||
#define _MEM_ALLOCATE_H
|
||||
|
||||
#include <malloc.h>
|
||||
|
||||
static inline void* ntfs_alloc (size_t size) {
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
static inline void* ntfs_align (size_t size) {
|
||||
#ifdef __wii__
|
||||
return memalign(32, size);
|
||||
#else
|
||||
return malloc(size);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void ntfs_free (void* mem) {
|
||||
free(mem);
|
||||
}
|
||||
|
||||
#endif /* _MEM_ALLOCATE_H */
|
1909
source/mft.c
Normal file
1909
source/mft.c
Normal file
File diff suppressed because it is too large
Load Diff
132
source/mft.h
Normal file
132
source/mft.h
Normal file
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* mft.h - Exports for MFT record handling. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2002 Anton Altaparmakov
|
||||
* Copyright (c) 2004-2005 Richard Russon
|
||||
* Copyright (c) 2006-2008 Szabolcs Szakacsits
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_MFT_H
|
||||
#define _NTFS_MFT_H
|
||||
|
||||
#include "volume.h"
|
||||
#include "inode.h"
|
||||
#include "layout.h"
|
||||
#include "logging.h"
|
||||
|
||||
extern int ntfs_mft_records_read(const ntfs_volume *vol, const MFT_REF mref,
|
||||
const s64 count, MFT_RECORD *b);
|
||||
|
||||
/**
|
||||
* ntfs_mft_record_read - read a record from the mft
|
||||
* @vol: volume to read from
|
||||
* @mref: mft record number to read
|
||||
* @b: output data buffer
|
||||
*
|
||||
* Read the mft record specified by @mref from volume @vol into buffer @b.
|
||||
* Return 0 on success or -1 on error, with errno set to the error code.
|
||||
*
|
||||
* The read mft record is mst deprotected and is hence ready to use. The caller
|
||||
* should check the record with is_baad_record() in case mst deprotection
|
||||
* failed.
|
||||
*
|
||||
* NOTE: @b has to be at least of size vol->mft_record_size.
|
||||
*/
|
||||
static __inline__ int ntfs_mft_record_read(const ntfs_volume *vol,
|
||||
const MFT_REF mref, MFT_RECORD *b)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ntfs_log_enter("Entering for inode %lld\n", (long long)MREF(mref));
|
||||
ret = ntfs_mft_records_read(vol, mref, 1, b);
|
||||
ntfs_log_leave("\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
extern int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref,
|
||||
MFT_RECORD *m);
|
||||
|
||||
extern int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref,
|
||||
MFT_RECORD **mrec, ATTR_RECORD **attr);
|
||||
|
||||
extern int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref,
|
||||
const s64 count, MFT_RECORD *b);
|
||||
|
||||
/**
|
||||
* ntfs_mft_record_write - write an mft record to disk
|
||||
* @vol: volume to write to
|
||||
* @mref: mft record number to write
|
||||
* @b: data buffer containing the mft record to write
|
||||
*
|
||||
* Write the mft record specified by @mref from buffer @b to volume @vol.
|
||||
* Return 0 on success or -1 on error, with errno set to the error code.
|
||||
*
|
||||
* Before the mft record is written, it is mst protected. After the write, it
|
||||
* is deprotected again, thus resulting in an increase in the update sequence
|
||||
* number inside the buffer @b.
|
||||
*
|
||||
* NOTE: @b has to be at least of size vol->mft_record_size.
|
||||
*/
|
||||
static __inline__ int ntfs_mft_record_write(const ntfs_volume *vol,
|
||||
const MFT_REF mref, MFT_RECORD *b)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ntfs_log_enter("Entering for inode %lld\n", (long long)MREF(mref));
|
||||
ret = ntfs_mft_records_write(vol, mref, 1, b);
|
||||
ntfs_log_leave("\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_mft_record_get_data_size - return number of bytes used in mft record @b
|
||||
* @m: mft record to get the data size of
|
||||
*
|
||||
* Takes the mft record @m and returns the number of bytes used in the record
|
||||
* or 0 on error (i.e. @m is not a valid mft record). Zero is not a valid size
|
||||
* for an mft record as it at least has to have the MFT_RECORD itself and a
|
||||
* zero length attribute of type AT_END, thus making the minimum size 56 bytes.
|
||||
*
|
||||
* Aside: The size is independent of NTFS versions 1.x/3.x because the 8-byte
|
||||
* alignment of the first attribute mask the difference in MFT_RECORD size
|
||||
* between NTFS 1.x and 3.x. Also, you would expect every mft record to
|
||||
* contain an update sequence array as well but that could in theory be
|
||||
* non-existent (don't know if Windows' NTFS driver/chkdsk wouldn't view this
|
||||
* as corruption in itself though).
|
||||
*/
|
||||
static __inline__ u32 ntfs_mft_record_get_data_size(const MFT_RECORD *m)
|
||||
{
|
||||
if (!m || !ntfs_is_mft_record(m->magic))
|
||||
return 0;
|
||||
/* Get the number of used bytes and return it. */
|
||||
return le32_to_cpu(m->bytes_in_use);
|
||||
}
|
||||
|
||||
extern int ntfs_mft_record_layout(const ntfs_volume *vol, const MFT_REF mref,
|
||||
MFT_RECORD *mrec);
|
||||
|
||||
extern int ntfs_mft_record_format(const ntfs_volume *vol, const MFT_REF mref);
|
||||
|
||||
extern ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni);
|
||||
|
||||
extern int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni);
|
||||
|
||||
extern int ntfs_mft_usn_dec(MFT_RECORD *mrec);
|
||||
|
||||
#endif /* defined _NTFS_MFT_H */
|
||||
|
61
source/misc.c
Normal file
61
source/misc.c
Normal file
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* misc.c : miscellaneous :
|
||||
* - dealing with errors in memory allocation
|
||||
*
|
||||
* Copyright (c) 2008 Jean-Pierre Andre
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "misc.h"
|
||||
#include "logging.h"
|
||||
|
||||
/**
|
||||
* ntfs_calloc
|
||||
*
|
||||
* Return a pointer to the allocated memory or NULL if the request fails.
|
||||
*/
|
||||
void *ntfs_calloc(size_t size)
|
||||
{
|
||||
void *p;
|
||||
|
||||
p = calloc(1, size);
|
||||
if (!p)
|
||||
ntfs_log_perror("Failed to calloc %lld bytes", (long long)size);
|
||||
return p;
|
||||
}
|
||||
|
||||
void *ntfs_malloc(size_t size)
|
||||
{
|
||||
void *p;
|
||||
|
||||
p = malloc(size);
|
||||
if (!p)
|
||||
ntfs_log_perror("Failed to malloc %lld bytes", (long long)size);
|
||||
return p;
|
||||
}
|
30
source/misc.h
Normal file
30
source/misc.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* misc.h : miscellaneous exports
|
||||
* - memory allocation
|
||||
*
|
||||
* Copyright (c) 2008 Jean-Pierre Andre
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_MISC_H_
|
||||
#define _NTFS_MISC_H_
|
||||
|
||||
void *ntfs_calloc(size_t size);
|
||||
void *ntfs_malloc(size_t size);
|
||||
|
||||
#endif /* _NTFS_MISC_H_ */
|
||||
|
231
source/mst.c
Normal file
231
source/mst.c
Normal file
@ -0,0 +1,231 @@
|
||||
/**
|
||||
* mst.c - Multi sector fixup handling code. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2004 Anton Altaparmakov
|
||||
* Copyright (c) 2006-2009 Szabolcs Szakacsits
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "mst.h"
|
||||
#include "logging.h"
|
||||
|
||||
/**
|
||||
* ntfs_mst_post_read_fixup - deprotect multi sector transfer protected data
|
||||
* @b: pointer to the data to deprotect
|
||||
* @size: size in bytes of @b
|
||||
*
|
||||
* Perform the necessary post read multi sector transfer fixups and detect the
|
||||
* presence of incomplete multi sector transfers. - In that case, overwrite the
|
||||
* magic of the ntfs record header being processed with "BAAD" (in memory only!)
|
||||
* and abort processing.
|
||||
*
|
||||
* Return 0 on success and -1 on error, with errno set to the error code. The
|
||||
* following error codes are defined:
|
||||
* EINVAL Invalid arguments or invalid NTFS record in buffer @b.
|
||||
* EIO Multi sector transfer error was detected. Magic of the NTFS
|
||||
* record in @b will have been set to "BAAD".
|
||||
*/
|
||||
int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size)
|
||||
{
|
||||
u16 usa_ofs, usa_count, usn;
|
||||
u16 *usa_pos, *data_pos;
|
||||
|
||||
ntfs_log_trace("Entering\n");
|
||||
|
||||
/* Setup the variables. */
|
||||
usa_ofs = le16_to_cpu(b->usa_ofs);
|
||||
/* Decrement usa_count to get number of fixups. */
|
||||
usa_count = le16_to_cpu(b->usa_count) - 1;
|
||||
/* Size and alignment checks. */
|
||||
if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 ||
|
||||
(u32)(usa_ofs + (usa_count * 2)) > size ||
|
||||
(size >> NTFS_BLOCK_SIZE_BITS) != usa_count) {
|
||||
errno = EINVAL;
|
||||
ntfs_log_perror("%s: magic: 0x%08x size: %d usa_ofs: %d "
|
||||
"usa_count: %d", __FUNCTION__, *(le32 *)b,
|
||||
size, usa_ofs, usa_count);
|
||||
return -1;
|
||||
}
|
||||
/* Position of usn in update sequence array. */
|
||||
usa_pos = (u16*)b + usa_ofs/sizeof(u16);
|
||||
/*
|
||||
* The update sequence number which has to be equal to each of the
|
||||
* u16 values before they are fixed up. Note no need to care for
|
||||
* endianness since we are comparing and moving data for on disk
|
||||
* structures which means the data is consistent. - If it is
|
||||
* consistency the wrong endianness it doesn't make any difference.
|
||||
*/
|
||||
usn = *usa_pos;
|
||||
/*
|
||||
* Position in protected data of first u16 that needs fixing up.
|
||||
*/
|
||||
data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
|
||||
/*
|
||||
* Check for incomplete multi sector transfer(s).
|
||||
*/
|
||||
while (usa_count--) {
|
||||
if (*data_pos != usn) {
|
||||
/*
|
||||
* Incomplete multi sector transfer detected! )-:
|
||||
* Set the magic to "BAAD" and return failure.
|
||||
* Note that magic_BAAD is already converted to le32.
|
||||
*/
|
||||
errno = EIO;
|
||||
ntfs_log_perror("Incomplete multi-sector transfer: "
|
||||
"magic: 0x%08x size: %d usa_ofs: %d usa_count:"
|
||||
" %d data: %d usn: %d", *(le32 *)b, size,
|
||||
usa_ofs, usa_count, *data_pos, usn);
|
||||
b->magic = magic_BAAD;
|
||||
return -1;
|
||||
}
|
||||
data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
|
||||
}
|
||||
/* Re-setup the variables. */
|
||||
usa_count = le16_to_cpu(b->usa_count) - 1;
|
||||
data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
|
||||
/* Fixup all sectors. */
|
||||
while (usa_count--) {
|
||||
/*
|
||||
* Increment position in usa and restore original data from
|
||||
* the usa into the data buffer.
|
||||
*/
|
||||
*data_pos = *(++usa_pos);
|
||||
/* Increment position in data as well. */
|
||||
data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_mst_pre_write_fixup - apply multi sector transfer protection
|
||||
* @b: pointer to the data to protect
|
||||
* @size: size in bytes of @b
|
||||
*
|
||||
* Perform the necessary pre write multi sector transfer fixup on the data
|
||||
* pointer to by @b of @size.
|
||||
*
|
||||
* Return 0 if fixups applied successfully or -1 if no fixups were performed
|
||||
* due to errors. In that case errno i set to the error code (EINVAL).
|
||||
*
|
||||
* NOTE: We consider the absence / invalidity of an update sequence array to
|
||||
* mean error. This means that you have to create a valid update sequence
|
||||
* array header in the ntfs record before calling this function, otherwise it
|
||||
* will fail (the header needs to contain the position of the update sequence
|
||||
* array together with the number of elements in the array). You also need to
|
||||
* initialise the update sequence number before calling this function
|
||||
* otherwise a random word will be used (whatever was in the record at that
|
||||
* position at that time).
|
||||
*/
|
||||
int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size)
|
||||
{
|
||||
u16 usa_ofs, usa_count, usn;
|
||||
u16 *usa_pos, *data_pos;
|
||||
|
||||
ntfs_log_trace("Entering\n");
|
||||
|
||||
/* Sanity check + only fixup if it makes sense. */
|
||||
if (!b || ntfs_is_baad_record(b->magic) ||
|
||||
ntfs_is_hole_record(b->magic)) {
|
||||
errno = EINVAL;
|
||||
ntfs_log_perror("%s: bad argument", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
/* Setup the variables. */
|
||||
usa_ofs = le16_to_cpu(b->usa_ofs);
|
||||
/* Decrement usa_count to get number of fixups. */
|
||||
usa_count = le16_to_cpu(b->usa_count) - 1;
|
||||
/* Size and alignment checks. */
|
||||
if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 ||
|
||||
(u32)(usa_ofs + (usa_count * 2)) > size ||
|
||||
(size >> NTFS_BLOCK_SIZE_BITS) != usa_count) {
|
||||
errno = EINVAL;
|
||||
ntfs_log_perror("%s", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
/* Position of usn in update sequence array. */
|
||||
usa_pos = (u16*)((u8*)b + usa_ofs);
|
||||
/*
|
||||
* Cyclically increment the update sequence number
|
||||
* (skipping 0 and -1, i.e. 0xffff).
|
||||
*/
|
||||
usn = le16_to_cpup(usa_pos) + 1;
|
||||
if (usn == 0xffff || !usn)
|
||||
usn = 1;
|
||||
usn = cpu_to_le16(usn);
|
||||
*usa_pos = usn;
|
||||
/* Position in data of first u16 that needs fixing up. */
|
||||
data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
|
||||
/* Fixup all sectors. */
|
||||
while (usa_count--) {
|
||||
/*
|
||||
* Increment the position in the usa and save the
|
||||
* original data from the data buffer into the usa.
|
||||
*/
|
||||
*(++usa_pos) = *data_pos;
|
||||
/* Apply fixup to data. */
|
||||
*data_pos = usn;
|
||||
/* Increment position in data as well. */
|
||||
data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_mst_post_write_fixup - deprotect multi sector transfer protected data
|
||||
* @b: pointer to the data to deprotect
|
||||
*
|
||||
* Perform the necessary post write multi sector transfer fixup, not checking
|
||||
* for any errors, because we assume we have just used
|
||||
* ntfs_mst_pre_write_fixup(), thus the data will be fine or we would never
|
||||
* have gotten here.
|
||||
*/
|
||||
void ntfs_mst_post_write_fixup(NTFS_RECORD *b)
|
||||
{
|
||||
u16 *usa_pos, *data_pos;
|
||||
|
||||
u16 usa_ofs = le16_to_cpu(b->usa_ofs);
|
||||
u16 usa_count = le16_to_cpu(b->usa_count) - 1;
|
||||
|
||||
ntfs_log_trace("Entering\n");
|
||||
|
||||
/* Position of usn in update sequence array. */
|
||||
usa_pos = (u16*)b + usa_ofs/sizeof(u16);
|
||||
|
||||
/* Position in protected data of first u16 that needs fixing up. */
|
||||
data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
|
||||
|
||||
/* Fixup all sectors. */
|
||||
while (usa_count--) {
|
||||
/*
|
||||
* Increment position in usa and restore original data from
|
||||
* the usa into the data buffer.
|
||||
*/
|
||||
*data_pos = *(++usa_pos);
|
||||
|
||||
/* Increment position in data as well. */
|
||||
data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
|
||||
}
|
||||
}
|
||||
|
34
source/mst.h
Normal file
34
source/mst.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* mst.h - Exports for multi sector transfer fixup functions.
|
||||
* Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2002 Anton Altaparmakov
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_MST_H
|
||||
#define _NTFS_MST_H
|
||||
|
||||
#include "types.h"
|
||||
#include "layout.h"
|
||||
|
||||
extern int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size);
|
||||
extern int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size);
|
||||
extern void ntfs_mst_post_write_fixup(NTFS_RECORD *b);
|
||||
|
||||
#endif /* defined _NTFS_MST_H */
|
||||
|
662
source/ntfs.c
Normal file
662
source/ntfs.c
Normal file
@ -0,0 +1,662 @@
|
||||
/**
|
||||
* ntfs.c - Simple functionality for startup, mounting and unmounting of NTFS-based devices.
|
||||
*
|
||||
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
||||
* Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "ntfs.h"
|
||||
#include "ntfsinternal.h"
|
||||
#include "ntfsfile.h"
|
||||
#include "ntfsdir.h"
|
||||
#include "gekko_io.h"
|
||||
#include "cache.h"
|
||||
|
||||
// NTFS device driver devoptab
|
||||
static const devoptab_t devops_ntfs = {
|
||||
NULL, /* Device name */
|
||||
sizeof (ntfs_file_state),
|
||||
ntfs_open_r,
|
||||
ntfs_close_r,
|
||||
ntfs_write_r,
|
||||
ntfs_read_r,
|
||||
ntfs_seek_r,
|
||||
ntfs_fstat_r,
|
||||
ntfs_stat_r,
|
||||
ntfs_link_r,
|
||||
ntfs_unlink_r,
|
||||
ntfs_chdir_r,
|
||||
ntfs_rename_r,
|
||||
ntfs_mkdir_r,
|
||||
sizeof (ntfs_dir_state),
|
||||
ntfs_diropen_r,
|
||||
ntfs_dirreset_r,
|
||||
ntfs_dirnext_r,
|
||||
ntfs_dirclose_r,
|
||||
ntfs_statvfs_r,
|
||||
ntfs_ftruncate_r,
|
||||
ntfs_fsync_r,
|
||||
NULL /* Device data */
|
||||
};
|
||||
|
||||
void ntfsInit (void)
|
||||
{
|
||||
static bool isInit = false;
|
||||
|
||||
// Initialise ntfs-3g (if not already done so)
|
||||
if (!isInit) {
|
||||
isInit = true;
|
||||
|
||||
// Set the log handler
|
||||
#ifdef NTFS_ENABLE_LOG
|
||||
ntfs_log_set_handler(ntfs_log_handler_stderr);
|
||||
#else
|
||||
ntfs_log_set_handler(ntfs_log_handler_null);
|
||||
#endif
|
||||
// Set our current local
|
||||
ntfs_set_locale();
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions)
|
||||
{
|
||||
MASTER_BOOT_RECORD mbr;
|
||||
PARTITION_RECORD *partition = NULL;
|
||||
sec_t partition_starts[NTFS_MAX_PARTITIONS] = {0};
|
||||
int partition_count = 0;
|
||||
sec_t part_lba = 0;
|
||||
int i;
|
||||
|
||||
union {
|
||||
u8 buffer[512];
|
||||
MASTER_BOOT_RECORD mbr;
|
||||
EXTENDED_BOOT_RECORD ebr;
|
||||
NTFS_BOOT_SECTOR boot;
|
||||
} sector;
|
||||
|
||||
// Sanity check
|
||||
if (!interface) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (!partitions)
|
||||
return 0;
|
||||
|
||||
// Initialise ntfs-3g
|
||||
ntfsInit();
|
||||
|
||||
// Start the device and check that it is inserted
|
||||
if (!interface->startup()) {
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
if (!interface->isInserted()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Read the first sector on the device
|
||||
if (!interface->readSectors(0, 1, §or.buffer)) {
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// If this is the devices master boot record
|
||||
if (sector.mbr.signature == MBR_SIGNATURE) {
|
||||
memcpy(&mbr, §or, sizeof(MASTER_BOOT_RECORD));
|
||||
ntfs_log_debug("Valid Master Boot Record found\n");
|
||||
|
||||
// Search the partition table for all NTFS partitions (max. 4 primary partitions)
|
||||
for (i = 0; i < 4; i++) {
|
||||
partition = &mbr.partitions[i];
|
||||
part_lba = le32_to_cpu(mbr.partitions[i].lba_start);
|
||||
|
||||
ntfs_log_debug("Partition %i: %s, sector %d, type 0x%x\n", i + 1,
|
||||
partition->status == PARTITION_STATUS_BOOTABLE ? "bootable (active)" : "non-bootable",
|
||||
part_lba, partition->type);
|
||||
|
||||
// Figure out what type of partition this is
|
||||
switch (partition->type) {
|
||||
|
||||
// Ignore empty partitions
|
||||
case PARTITION_TYPE_EMPTY:
|
||||
continue;
|
||||
|
||||
// NTFS partition
|
||||
case PARTITION_TYPE_NTFS: {
|
||||
ntfs_log_debug("Partition %i: Claims to be NTFS\n", i + 1);
|
||||
|
||||
// Read and validate the NTFS partition
|
||||
if (interface->readSectors(part_lba, 1, §or)) {
|
||||
if (sector.boot.oem_id == NTFS_OEM_ID) {
|
||||
ntfs_log_debug("Partition %i: Valid NTFS boot sector found\n", i + 1);
|
||||
if (partition_count < NTFS_MAX_PARTITIONS) {
|
||||
partition_starts[partition_count] = part_lba;
|
||||
partition_count++;
|
||||
}
|
||||
} else {
|
||||
ntfs_log_debug("Partition %i: Invalid NTFS boot sector, not actually NTFS\n", i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
// DOS 3.3+ or Windows 95 extended partition
|
||||
case PARTITION_TYPE_DOS33_EXTENDED:
|
||||
case PARTITION_TYPE_WIN95_EXTENDED: {
|
||||
ntfs_log_debug("Partition %i: Claims to be Extended\n", i + 1);
|
||||
|
||||
// Walk the extended partition chain, finding all NTFS partitions within it
|
||||
sec_t ebr_lba = part_lba;
|
||||
sec_t next_erb_lba = 0;
|
||||
do {
|
||||
|
||||
// Read and validate the extended boot record
|
||||
if (interface->readSectors(ebr_lba + next_erb_lba, 1, §or)) {
|
||||
if (sector.ebr.signature == EBR_SIGNATURE) {
|
||||
ntfs_log_debug("Logical Partition @ %d: type 0x%x\n", ebr_lba + next_erb_lba,
|
||||
sector.ebr.partition.status == PARTITION_STATUS_BOOTABLE ? "bootable (active)" : "non-bootable",
|
||||
sector.ebr.partition.type);
|
||||
|
||||
// Get the start sector of the current partition
|
||||
// and the next extended boot record in the chain
|
||||
part_lba = ebr_lba + next_erb_lba + le32_to_cpu(sector.ebr.partition.lba_start);
|
||||
next_erb_lba = le32_to_cpu(sector.ebr.next_ebr.lba_start);
|
||||
|
||||
// Check if this partition has a valid NTFS boot record
|
||||
if (interface->readSectors(part_lba, 1, §or)) {
|
||||
if (sector.boot.oem_id == NTFS_OEM_ID) {
|
||||
ntfs_log_debug("Logical Partition @ %d: Valid NTFS boot sector found\n", part_lba);
|
||||
if(sector.ebr.partition.type != PARTITION_TYPE_NTFS) {
|
||||
ntfs_log_warning("Logical Partition @ %d: Is NTFS but type is 0x%x; 0x%x was expected\n", part_lba, sector.ebr.partition.type, PARTITION_TYPE_NTFS);
|
||||
}
|
||||
if (partition_count < NTFS_MAX_PARTITIONS) {
|
||||
partition_starts[partition_count] = part_lba;
|
||||
partition_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
next_erb_lba = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} while (next_erb_lba);
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
// Unknown or unsupported partition type
|
||||
default: {
|
||||
|
||||
// Check if this partition has a valid NTFS boot record anyway,
|
||||
// it might be misrepresented due to a lazy partition editor
|
||||
if (interface->readSectors(part_lba, 1, §or)) {
|
||||
if (sector.boot.oem_id == NTFS_OEM_ID) {
|
||||
ntfs_log_debug("Partition %i: Valid NTFS boot sector found\n", i + 1);
|
||||
if(partition->type != PARTITION_TYPE_NTFS) {
|
||||
ntfs_log_warning("Partition %i: Is NTFS but type is 0x%x; 0x%x was expected\n", i + 1, partition->type, PARTITION_TYPE_NTFS);
|
||||
}
|
||||
if (partition_count < NTFS_MAX_PARTITIONS) {
|
||||
partition_starts[partition_count] = part_lba;
|
||||
partition_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Else it is assumed this device has no master boot record
|
||||
} else {
|
||||
ntfs_log_debug("No Master Boot Record was found!\n");
|
||||
|
||||
// As a last-ditched effort, search the first 64 sectors of the device for stray NTFS partitions
|
||||
for (i = 0; i < 64; i++) {
|
||||
if (interface->readSectors(i, 1, §or)) {
|
||||
if (sector.boot.oem_id == NTFS_OEM_ID) {
|
||||
ntfs_log_debug("Valid NTFS boot sector found at sector %d!\n", i);
|
||||
if (partition_count < NTFS_MAX_PARTITIONS) {
|
||||
partition_starts[partition_count] = i;
|
||||
partition_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Return the found partitions (if any)
|
||||
if (partition_count > 0) {
|
||||
*partitions = (sec_t*)ntfs_alloc(sizeof(sec_t) * partition_count);
|
||||
if (*partitions) {
|
||||
memcpy(*partitions, &partition_starts, sizeof(sec_t) * partition_count);
|
||||
return partition_count;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ntfsMountAll (ntfs_md **mounts, u32 flags)
|
||||
{
|
||||
const INTERFACE_ID *discs = ntfsGetDiscInterfaces();
|
||||
const INTERFACE_ID *disc = NULL;
|
||||
ntfs_md mount_points[NTFS_MAX_MOUNTS];
|
||||
sec_t *partitions = NULL;
|
||||
int mount_count = 0;
|
||||
int partition_count = 0;
|
||||
char name[128];
|
||||
int i, j, k;
|
||||
|
||||
// Initialise ntfs-3g
|
||||
ntfsInit();
|
||||
|
||||
// Find and mount all NTFS partitions on all known devices
|
||||
for (i = 0; discs[i].name != NULL && discs[i].interface != NULL; i++) {
|
||||
disc = &discs[i];
|
||||
partition_count = ntfsFindPartitions(disc->interface, &partitions);
|
||||
if (partition_count > 0 && partitions) {
|
||||
for (j = 0, k = 0; j < partition_count; j++) {
|
||||
|
||||
// Find the next unused mount name
|
||||
do {
|
||||
sprintf(name, "%s%i", NTFS_MOUNT_PREFIX, k++);
|
||||
if (k >= NTFS_MAX_MOUNTS) {
|
||||
ntfs_free(partitions);
|
||||
errno = EADDRNOTAVAIL;
|
||||
return -1;
|
||||
}
|
||||
} while (ntfsGetDevice(name, false));
|
||||
|
||||
// Mount the partition
|
||||
if (mount_count < NTFS_MAX_MOUNTS) {
|
||||
if (ntfsMount(name, disc->interface, partitions[j], CACHE_DEFAULT_PAGE_SIZE, CACHE_DEFAULT_PAGE_COUNT, flags)) {
|
||||
strcpy(mount_points[mount_count].name, name);
|
||||
mount_points[mount_count].interface = disc->interface;
|
||||
mount_points[mount_count].startSector = partitions[j];
|
||||
mount_count++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
ntfs_free(partitions);
|
||||
}
|
||||
}
|
||||
|
||||
// Return the mounts (if any)
|
||||
if (mount_count > 0 && mounts) {
|
||||
*mounts = (ntfs_md*)ntfs_alloc(sizeof(ntfs_md) * mount_count);
|
||||
if (*mounts) {
|
||||
memcpy(*mounts, &mount_points, sizeof(ntfs_md) * mount_count);
|
||||
return mount_count;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ntfsMountDevice (const DISC_INTERFACE *interface, ntfs_md **mounts, u32 flags)
|
||||
{
|
||||
const INTERFACE_ID *discs = ntfsGetDiscInterfaces();
|
||||
const INTERFACE_ID *disc = NULL;
|
||||
ntfs_md mount_points[NTFS_MAX_MOUNTS];
|
||||
sec_t *partitions = NULL;
|
||||
int mount_count = 0;
|
||||
int partition_count = 0;
|
||||
char name[128];
|
||||
int i, j, k;
|
||||
|
||||
// Sanity check
|
||||
if (!interface) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Initialise ntfs-3g
|
||||
ntfsInit();
|
||||
|
||||
// Find the specified device then find and mount all NTFS partitions on it
|
||||
for (i = 0; discs[i].name != NULL && discs[i].interface != NULL; i++) {
|
||||
if (discs[i].interface == interface) {
|
||||
disc = &discs[i];
|
||||
partition_count = ntfsFindPartitions(disc->interface, &partitions);
|
||||
if (partition_count > 0 && partitions) {
|
||||
for (j = 0, k = 0; j < partition_count; j++) {
|
||||
|
||||
// Find the next unused mount name
|
||||
do {
|
||||
sprintf(name, "%s%i", NTFS_MOUNT_PREFIX, k++);
|
||||
if (k >= NTFS_MAX_MOUNTS) {
|
||||
ntfs_free(partitions);
|
||||
errno = EADDRNOTAVAIL;
|
||||
return -1;
|
||||
}
|
||||
} while (ntfsGetDevice(name, false));
|
||||
|
||||
// Mount the partition
|
||||
if (mount_count < NTFS_MAX_MOUNTS) {
|
||||
if (ntfsMount(name, disc->interface, partitions[j], CACHE_DEFAULT_PAGE_SIZE, CACHE_DEFAULT_PAGE_COUNT, flags)) {
|
||||
strcpy(mount_points[mount_count].name, name);
|
||||
mount_points[mount_count].interface = disc->interface;
|
||||
mount_points[mount_count].startSector = partitions[j];
|
||||
mount_count++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
ntfs_free(partitions);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we couldn't find the device then return with error status
|
||||
if (!disc) {
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Return the mounts (if any)
|
||||
if (mount_count > 0 && mounts) {
|
||||
*mounts = (ntfs_md*)ntfs_alloc(sizeof(ntfs_md) * mount_count);
|
||||
if (*mounts) {
|
||||
memcpy(*mounts, &mount_points, sizeof(ntfs_md) * mount_count);
|
||||
return mount_count;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSector, u32 cachePageCount, u32 cachePageSize, u32 flags)
|
||||
{
|
||||
ntfs_vd *vd = NULL;
|
||||
gekko_fd *fd = NULL;
|
||||
|
||||
// Sanity check
|
||||
if (!name || !interface) {
|
||||
errno = EINVAL;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialise ntfs-3g
|
||||
ntfsInit();
|
||||
|
||||
// Check that the requested mount name is free
|
||||
if (ntfsGetDevice(name, false)) {
|
||||
errno = EADDRINUSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that we can at least read from this device
|
||||
if (!(interface->features & FEATURE_MEDIUM_CANREAD)) {
|
||||
errno = EPERM;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allocate the volume descriptor
|
||||
vd = (ntfs_vd*)ntfs_alloc(sizeof(ntfs_vd));
|
||||
if (!vd) {
|
||||
errno = ENOMEM;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Setup the volume descriptor
|
||||
vd->id = interface->ioType;
|
||||
vd->flags = 0;
|
||||
vd->uid = 0;
|
||||
vd->gid = 0;
|
||||
vd->fmask = 0;
|
||||
vd->dmask = 0;
|
||||
vd->atime = ((flags & NTFS_UPDATE_ACCESS_TIMES) ? ATIME_ENABLED : ATIME_DISABLED);
|
||||
vd->showHiddenFiles = (flags & NTFS_SHOW_HIDDEN_FILES);
|
||||
vd->showSystemFiles = (flags & NTFS_SHOW_SYSTEM_FILES);
|
||||
|
||||
// Allocate the device driver descriptor
|
||||
fd = (gekko_fd*)ntfs_alloc(sizeof(gekko_fd));
|
||||
if (!fd) {
|
||||
ntfs_free(vd);
|
||||
errno = ENOMEM;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Setup the device driver descriptor
|
||||
fd->interface = interface;
|
||||
fd->startSector = startSector;
|
||||
fd->sectorSize = 0;
|
||||
fd->sectorCount = 0;
|
||||
fd->cachePageCount = cachePageCount;
|
||||
fd->cachePageSize = cachePageSize;
|
||||
|
||||
// Allocate the device driver
|
||||
vd->dev = ntfs_device_alloc(name, 0, &ntfs_device_gekko_io_ops, fd);
|
||||
if (!vd->dev) {
|
||||
ntfs_free(fd);
|
||||
ntfs_free(vd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Build the mount flags
|
||||
if (flags & NTFS_READ_ONLY)
|
||||
vd->flags |= MS_RDONLY;
|
||||
else
|
||||
{
|
||||
if (!(interface->features & FEATURE_MEDIUM_CANWRITE))
|
||||
vd->flags |= MS_RDONLY;
|
||||
if ((interface->features & FEATURE_MEDIUM_CANREAD) && (interface->features & FEATURE_MEDIUM_CANWRITE))
|
||||
vd->flags |= MS_EXCLUSIVE;
|
||||
}
|
||||
if (flags & NTFS_RECOVER)
|
||||
vd->flags |= MS_RECOVER;
|
||||
if (flags & NTFS_IGNORE_HIBERFILE)
|
||||
vd->flags |= MS_IGNORE_HIBERFILE;
|
||||
|
||||
if (vd->flags & MS_RDONLY)
|
||||
ntfs_log_debug("Mounting \"%s\" as read-only\n", name);
|
||||
|
||||
// Mount the device
|
||||
vd->vol = ntfs_device_mount(vd->dev, vd->flags);
|
||||
if (!vd->vol) {
|
||||
switch(ntfs_volume_error(errno)) {
|
||||
case NTFS_VOLUME_NOT_NTFS: errno = EINVALPART; break;
|
||||
case NTFS_VOLUME_CORRUPT: errno = EINVALPART; break;
|
||||
case NTFS_VOLUME_HIBERNATED: errno = EHIBERNATED; break;
|
||||
case NTFS_VOLUME_UNCLEAN_UNMOUNT: errno = EDIRTY; break;
|
||||
default: errno = EINVAL; break;
|
||||
}
|
||||
ntfs_device_free(vd->dev);
|
||||
ntfs_free(vd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flags & NTFS_IGNORE_CASE)
|
||||
ntfs_set_ignore_case(vd->vol);
|
||||
|
||||
// Initialise the volume descriptor
|
||||
if (ntfsInitVolume(vd)) {
|
||||
ntfs_umount(vd->vol, true);
|
||||
ntfs_free(vd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add the device to the devoptab table
|
||||
if (ntfsAddDevice(name, vd)) {
|
||||
ntfsDeinitVolume(vd);
|
||||
ntfs_umount(vd->vol, true);
|
||||
ntfs_free(vd);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ntfsUnmount (const char *name, bool force)
|
||||
{
|
||||
ntfs_vd *vd = NULL;
|
||||
|
||||
// Get the devices volume descriptor
|
||||
vd = ntfsGetVolume(name);
|
||||
if (!vd)
|
||||
return;
|
||||
|
||||
// Remove the device from the devoptab table
|
||||
ntfsRemoveDevice(name);
|
||||
|
||||
// Deinitialise the volume descriptor
|
||||
ntfsDeinitVolume(vd);
|
||||
|
||||
// Unmount the volume
|
||||
ntfs_umount(vd->vol, force);
|
||||
|
||||
// Free the volume descriptor
|
||||
ntfs_free(vd);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const char *ntfsGetVolumeName (const char *name)
|
||||
{
|
||||
ntfs_vd *vd = NULL;
|
||||
|
||||
// Sanity check
|
||||
if (!name) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get the devices volume descriptor
|
||||
vd = ntfsGetVolume(name);
|
||||
if (!vd) {
|
||||
errno = ENODEV;
|
||||
return NULL;
|
||||
}
|
||||
return vd->vol->vol_name;
|
||||
}
|
||||
|
||||
bool ntfsSetVolumeName (const char *name, const char *volumeName)
|
||||
{
|
||||
ntfs_vd *vd = NULL;
|
||||
ntfs_attr *na = NULL;
|
||||
ntfschar *ulabel = NULL;
|
||||
int ulabel_len;
|
||||
|
||||
// Sanity check
|
||||
if (!name) {
|
||||
errno = EINVAL;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the devices volume descriptor
|
||||
vd = ntfsGetVolume(name);
|
||||
if (!vd) {
|
||||
errno = ENODEV;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Lock
|
||||
ntfsLock(vd);
|
||||
|
||||
// Convert the new volume name to unicode
|
||||
ulabel_len = ntfsLocalToUnicode(volumeName, &ulabel) * sizeof(ntfschar);
|
||||
if (ulabel_len < 0) {
|
||||
ntfsUnlock(vd);
|
||||
errno = EINVAL;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the volume name attribute exists
|
||||
na = ntfs_attr_open(vd->vol->vol_ni, AT_VOLUME_NAME, NULL, 0);
|
||||
if (na) {
|
||||
|
||||
// It does, resize it to match the length of the new volume name
|
||||
if (ntfs_attr_truncate(na, ulabel_len)) {
|
||||
free(ulabel);
|
||||
ntfsUnlock(vd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write the new volume name
|
||||
if (ntfs_attr_pwrite(na, 0, ulabel_len, ulabel) != ulabel_len) {
|
||||
free(ulabel);
|
||||
ntfsUnlock(vd);
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// It doesn't, create it now
|
||||
if (ntfs_attr_add(vd->vol->vol_ni, AT_VOLUME_NAME, NULL, 0, (u8*)ulabel, ulabel_len)) {
|
||||
free(ulabel);
|
||||
ntfsUnlock(vd);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Reset the volumes name cache (as it has now been changed)
|
||||
vd->name[0] = '\0';
|
||||
|
||||
// Close the volume name attribute
|
||||
if (na)
|
||||
ntfs_attr_close(na);
|
||||
|
||||
// Sync the volume node
|
||||
if (ntfs_inode_sync(vd->vol->vol_ni)) {
|
||||
free(ulabel);
|
||||
ntfsUnlock(vd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clean up
|
||||
free(ulabel);
|
||||
|
||||
// Unlock
|
||||
ntfsUnlock(vd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const devoptab_t *ntfsGetDevOpTab (void)
|
||||
{
|
||||
return &devops_ntfs;
|
||||
}
|
636
source/ntfsdir.c
Normal file
636
source/ntfsdir.c
Normal file
@ -0,0 +1,636 @@
|
||||
/**
|
||||
* ntfs_dir.c - devoptab directory routines for NTFS-based devices.
|
||||
*
|
||||
* Copyright (c) 2010 Dimok
|
||||
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
||||
* Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_STATVFS_H
|
||||
#include <sys/statvfs.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "ntfsinternal.h"
|
||||
#include "ntfsdir.h"
|
||||
#include "device.h"
|
||||
#include <sys/dir.h>
|
||||
|
||||
#define STATE(x) ((ntfs_dir_state*)(x)->dirStruct)
|
||||
|
||||
void ntfsCloseDir (ntfs_dir_state *dir)
|
||||
{
|
||||
// Sanity check
|
||||
if (!dir || !dir->vd)
|
||||
return;
|
||||
|
||||
// Free the directory entries (if any)
|
||||
while (dir->first) {
|
||||
ntfs_dir_entry *next = dir->first->next;
|
||||
ntfs_free(dir->first->name);
|
||||
ntfs_free(dir->first);
|
||||
dir->first = next;
|
||||
}
|
||||
|
||||
// Close the directory (if open)
|
||||
if (dir->ni)
|
||||
ntfsCloseEntry(dir->vd, dir->ni);
|
||||
|
||||
// Reset the directory state
|
||||
dir->ni = NULL;
|
||||
dir->first = NULL;
|
||||
dir->current = NULL;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int ntfs_stat_r (struct _reent *r, const char *path, struct stat *st)
|
||||
{
|
||||
// Short circuit cases were we don't actually have to do anything
|
||||
if (!st || !path)
|
||||
return 0;
|
||||
|
||||
ntfs_log_trace("path %s, st %p\n", path, st);
|
||||
|
||||
ntfs_vd *vd = NULL;
|
||||
ntfs_inode *ni = NULL;
|
||||
|
||||
// Get the volume descriptor for this path
|
||||
vd = ntfsGetVolume(path);
|
||||
if (!vd) {
|
||||
r->_errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(strcmp(path, ".") == 0 || strcmp(path, "..") == 0)
|
||||
{
|
||||
memset(st, 0, sizeof(struct stat));
|
||||
st->st_mode = S_IFDIR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Lock
|
||||
ntfsLock(vd);
|
||||
|
||||
// Find the entry
|
||||
ni = ntfsOpenEntry(vd, path);
|
||||
if (!ni) {
|
||||
r->_errno = errno;
|
||||
ntfsUnlock(vd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get the entry stats
|
||||
int ret = ntfsStat(vd, ni, st);
|
||||
if (ret)
|
||||
r->_errno = errno;
|
||||
|
||||
// Close the entry
|
||||
ntfsCloseEntry(vd, ni);
|
||||
|
||||
ntfsUnlock(vd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ntfs_link_r (struct _reent *r, const char *existing, const char *newLink)
|
||||
{
|
||||
ntfs_log_trace("existing %s, newLink %s\n", existing, newLink);
|
||||
|
||||
ntfs_vd *vd = NULL;
|
||||
ntfs_inode *ni = NULL;
|
||||
|
||||
// Get the volume descriptor for this path
|
||||
vd = ntfsGetVolume(existing);
|
||||
if (!vd) {
|
||||
r->_errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Lock
|
||||
ntfsLock(vd);
|
||||
|
||||
// Create a symbolic link between the two paths
|
||||
ni = ntfsCreate(vd, existing, S_IFLNK, newLink);
|
||||
if (!ni) {
|
||||
ntfsUnlock(vd);
|
||||
r->_errno = errno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Close the symbolic link
|
||||
ntfsCloseEntry(vd, ni);
|
||||
|
||||
// Unlock
|
||||
ntfsUnlock(vd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ntfs_unlink_r (struct _reent *r, const char *name)
|
||||
{
|
||||
ntfs_log_trace("name %s\n", name);
|
||||
|
||||
// Unlink the entry
|
||||
int ret = ntfsUnlink(ntfsGetVolume(name), name);
|
||||
if (ret)
|
||||
r->_errno = errno;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ntfs_chdir_r (struct _reent *r, const char *name)
|
||||
{
|
||||
ntfs_log_trace("name %s\n", name);
|
||||
|
||||
ntfs_vd *vd = NULL;
|
||||
ntfs_inode *ni = NULL;
|
||||
|
||||
// Get the volume descriptor for this path
|
||||
vd = ntfsGetVolume(name);
|
||||
if (!vd) {
|
||||
r->_errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Lock
|
||||
ntfsLock(vd);
|
||||
|
||||
// Find the directory
|
||||
ni = ntfsOpenEntry(vd, name);
|
||||
if (!ni) {
|
||||
ntfsUnlock(vd);
|
||||
r->_errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Ensure that this directory is indeed a directory
|
||||
if (!(ni->mrec->flags && MFT_RECORD_IS_DIRECTORY)) {
|
||||
ntfsCloseEntry(vd, ni);
|
||||
ntfsUnlock(vd);
|
||||
r->_errno = ENOTDIR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Close the old current directory (if any)
|
||||
if (vd->cwd_ni)
|
||||
ntfsCloseEntry(vd, vd->cwd_ni);
|
||||
|
||||
// Set the new current directory
|
||||
vd->cwd_ni = ni;
|
||||
|
||||
// Unlock
|
||||
ntfsUnlock(vd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ntfs_rename_r (struct _reent *r, const char *oldName, const char *newName)
|
||||
{
|
||||
ntfs_log_trace("oldName %s, newName %s\n", oldName, newName);
|
||||
|
||||
ntfs_vd *vd = NULL;
|
||||
ntfs_inode *ni = NULL;
|
||||
|
||||
// Get the volume descriptor for this path
|
||||
vd = ntfsGetVolume(oldName);
|
||||
if (!vd) {
|
||||
r->_errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Lock
|
||||
ntfsLock(vd);
|
||||
|
||||
// You cannot rename between devices
|
||||
if(vd != ntfsGetVolume(newName)) {
|
||||
ntfsUnlock(vd);
|
||||
r->_errno = EXDEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check that there is no existing entry with the new name
|
||||
ni = ntfsOpenEntry(vd, newName);
|
||||
if (ni) {
|
||||
ntfsCloseEntry(vd, ni);
|
||||
ntfsUnlock(vd);
|
||||
r->_errno = EEXIST;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Link the old entry with the new one
|
||||
if (ntfsLink(vd, oldName, newName)) {
|
||||
ntfsUnlock(vd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Unlink the old entry
|
||||
if (ntfsUnlink(vd, oldName)) {
|
||||
if (ntfsUnlink(vd, newName)) {
|
||||
ntfsUnlock(vd);
|
||||
return -1;
|
||||
}
|
||||
ntfsUnlock(vd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Unlock
|
||||
ntfsUnlock(vd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ntfs_mkdir_r (struct _reent *r, const char *path, int mode)
|
||||
{
|
||||
ntfs_log_trace("path %s, mode %i\n", path, mode);
|
||||
|
||||
ntfs_vd *vd = NULL;
|
||||
ntfs_inode *ni = NULL;
|
||||
|
||||
// Get the volume descriptor for this path
|
||||
vd = ntfsGetVolume(path);
|
||||
if (!vd) {
|
||||
r->_errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Lock
|
||||
ntfsLock(vd);
|
||||
|
||||
// Create the directory
|
||||
ni = ntfsCreate(vd, path, S_IFDIR, NULL);
|
||||
if (!ni) {
|
||||
ntfsUnlock(vd);
|
||||
r->_errno = errno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Close the directory
|
||||
ntfsCloseEntry(vd, ni);
|
||||
|
||||
// Unlock
|
||||
ntfsUnlock(vd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ntfs_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf)
|
||||
{
|
||||
ntfs_log_trace("path %s, buf %p\n", path, buf);
|
||||
|
||||
ntfs_vd *vd = NULL;
|
||||
s64 size;
|
||||
int delta_bits;
|
||||
|
||||
// Get the volume descriptor for this path
|
||||
vd = ntfsGetVolume(path);
|
||||
if (!vd) {
|
||||
r->_errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Short circuit cases were we don't actually have to do anything
|
||||
if (!buf)
|
||||
return 0;
|
||||
|
||||
// Lock
|
||||
ntfsLock(vd);
|
||||
|
||||
// Zero out the stat buffer
|
||||
memset(buf, 0, sizeof(struct statvfs));
|
||||
|
||||
if(ntfs_volume_get_free_space(vd->vol) < 0)
|
||||
{
|
||||
ntfsUnlock(vd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// File system block size
|
||||
buf->f_bsize = vd->vol->cluster_size;
|
||||
|
||||
// Fundamental file system block size
|
||||
buf->f_frsize = vd->vol->cluster_size;
|
||||
|
||||
// Total number of blocks on file system in units of f_frsize
|
||||
buf->f_blocks = vd->vol->nr_clusters;
|
||||
|
||||
// Free blocks available for all and for non-privileged processes
|
||||
size = MAX(vd->vol->free_clusters, 0);
|
||||
buf->f_bfree = buf->f_bavail = size;
|
||||
|
||||
// Free inodes on the free space
|
||||
delta_bits = vd->vol->cluster_size_bits - vd->vol->mft_record_size_bits;
|
||||
if (delta_bits >= 0)
|
||||
size <<= delta_bits;
|
||||
else
|
||||
size >>= -delta_bits;
|
||||
|
||||
// Number of inodes at this point in time
|
||||
buf->f_files = (vd->vol->mftbmp_na->allocated_size << 3) + size;
|
||||
|
||||
// Free inodes available for all and for non-privileged processes
|
||||
size += vd->vol->free_mft_records;
|
||||
buf->f_ffree = buf->f_favail = MAX(size, 0);
|
||||
|
||||
// File system id
|
||||
buf->f_fsid = vd->id;
|
||||
|
||||
// Bit mask of f_flag values.
|
||||
buf->f_flag = (NVolReadOnly(vd->vol) ? ST_RDONLY : 0);
|
||||
|
||||
// Maximum length of filenames
|
||||
buf->f_namemax = NTFS_MAX_NAME_LEN;
|
||||
|
||||
// Unlock
|
||||
ntfsUnlock(vd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* PRIVATE: Callback for directory walking
|
||||
*/
|
||||
int ntfs_readdir_filler (DIR_ITER *dirState, const ntfschar *name, const int name_len, const int name_type,
|
||||
const s64 pos, const MFT_REF mref, const unsigned dt_type)
|
||||
{
|
||||
ntfs_dir_state *dir = STATE(dirState);
|
||||
ntfs_dir_entry *entry = NULL;
|
||||
char *entry_name = NULL;
|
||||
|
||||
// Sanity check
|
||||
if (!dir || !dir->vd) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Ignore DOS file names
|
||||
if (name_type == FILE_NAME_DOS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Preliminary check that this entry can be enumerated (as described by the volume descriptor)
|
||||
if (MREF(mref) == FILE_root || MREF(mref) >= FILE_first_user || dir->vd->showSystemFiles) {
|
||||
|
||||
// Convert the entry name to our current local
|
||||
if (ntfsUnicodeToLocal(name, name_len, &entry_name, 0) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(dir->first && dir->first->mref == FILE_root &&
|
||||
MREF(mref) == FILE_root && strcmp(entry_name, "..") == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If this is not the parent or self directory reference
|
||||
if ((strcmp(entry_name, ".") != 0) && (strcmp(entry_name, "..") != 0)) {
|
||||
|
||||
// Open the entry
|
||||
ntfs_inode *ni = ntfs_pathname_to_inode(dir->vd->vol, dir->ni, entry_name);
|
||||
if (!ni)
|
||||
return -1;
|
||||
|
||||
// Double check that this entry can be emuerated (as described by the volume descriptor)
|
||||
if (((ni->flags & FILE_ATTR_HIDDEN) && !dir->vd->showHiddenFiles) ||
|
||||
((ni->flags & FILE_ATTR_SYSTEM) && !dir->vd->showSystemFiles)) {
|
||||
ntfs_inode_close(ni);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Close the entry
|
||||
ntfs_inode_close(ni);
|
||||
|
||||
}
|
||||
|
||||
// Allocate a new directory entry
|
||||
entry = (ntfs_dir_entry *) ntfs_alloc(sizeof(ntfs_dir_entry));
|
||||
if (!entry)
|
||||
return -1;
|
||||
|
||||
// Setup the entry
|
||||
entry->name = entry_name;
|
||||
entry->next = NULL;
|
||||
entry->mref = MREF(mref);
|
||||
|
||||
// Link the entry to the directory
|
||||
if (!dir->first) {
|
||||
dir->first = entry;
|
||||
} else {
|
||||
ntfs_dir_entry *last = dir->first;
|
||||
while (last->next) last = last->next;
|
||||
last->next = entry;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DIR_ITER *ntfs_diropen_r (struct _reent *r, DIR_ITER *dirState, const char *path)
|
||||
{
|
||||
ntfs_log_trace("dirState %p, path %s\n", dirState, path);
|
||||
|
||||
ntfs_dir_state* dir = STATE(dirState);
|
||||
s64 position = 0;
|
||||
|
||||
// Get the volume descriptor for this path
|
||||
dir->vd = ntfsGetVolume(path);
|
||||
if (!dir->vd) {
|
||||
r->_errno = ENODEV;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Lock
|
||||
ntfsLock(dir->vd);
|
||||
|
||||
// Find the directory
|
||||
dir->ni = ntfsOpenEntry(dir->vd, path);
|
||||
if (!dir->ni) {
|
||||
ntfsUnlock(dir->vd);
|
||||
r->_errno = ENOENT;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Ensure that this directory is indeed a directory
|
||||
if (!(dir->ni->mrec->flags && MFT_RECORD_IS_DIRECTORY)) {
|
||||
ntfsCloseEntry(dir->vd, dir->ni);
|
||||
ntfsUnlock(dir->vd);
|
||||
r->_errno = ENOTDIR;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Read the directory
|
||||
dir->first = dir->current = NULL;
|
||||
if (ntfs_readdir(dir->ni, &position, dirState, (ntfs_filldir_t)ntfs_readdir_filler)) {
|
||||
ntfsCloseDir(dir);
|
||||
ntfsUnlock(dir->vd);
|
||||
r->_errno = errno;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Move to the first entry in the directory
|
||||
dir->current = dir->first;
|
||||
|
||||
// Update directory times
|
||||
ntfsUpdateTimes(dir->vd, dir->ni, NTFS_UPDATE_ATIME);
|
||||
|
||||
// Insert the directory into the double-linked FILO list of open directories
|
||||
if (dir->vd->firstOpenDir) {
|
||||
dir->nextOpenDir = dir->vd->firstOpenDir;
|
||||
dir->vd->firstOpenDir->prevOpenDir = dir;
|
||||
} else {
|
||||
dir->nextOpenDir = NULL;
|
||||
}
|
||||
dir->prevOpenDir = NULL;
|
||||
dir->vd->cwd_ni = dir->ni;
|
||||
dir->vd->firstOpenDir = dir;
|
||||
dir->vd->openDirCount++;
|
||||
|
||||
// Unlock
|
||||
ntfsUnlock(dir->vd);
|
||||
|
||||
return dirState;
|
||||
}
|
||||
|
||||
int ntfs_dirreset_r (struct _reent *r, DIR_ITER *dirState)
|
||||
{
|
||||
ntfs_log_trace("dirState %p\n", dirState);
|
||||
|
||||
ntfs_dir_state* dir = STATE(dirState);
|
||||
|
||||
// Sanity check
|
||||
if (!dir || !dir->vd || !dir->ni) {
|
||||
r->_errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Lock
|
||||
ntfsLock(dir->vd);
|
||||
|
||||
// Move to the first entry in the directory
|
||||
dir->current = dir->first;
|
||||
|
||||
// Update directory times
|
||||
ntfsUpdateTimes(dir->vd, dir->ni, NTFS_UPDATE_ATIME);
|
||||
|
||||
// Unlock
|
||||
ntfsUnlock(dir->vd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ntfs_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat)
|
||||
{
|
||||
ntfs_log_trace("dirState %p, filename %p, filestat %p\n", dirState, filename, filestat);
|
||||
|
||||
ntfs_dir_state* dir = STATE(dirState);
|
||||
ntfs_inode *ni = NULL;
|
||||
|
||||
// Sanity check
|
||||
if (!dir || !dir->vd || !dir->ni) {
|
||||
r->_errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Lock
|
||||
ntfsLock(dir->vd);
|
||||
|
||||
// Check that there is a entry waiting to be fetched
|
||||
if (!dir->current) {
|
||||
ntfsUnlock(dir->vd);
|
||||
r->_errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Fetch the current entry
|
||||
strcpy(filename, dir->current->name);
|
||||
if(filestat != NULL)
|
||||
{
|
||||
if(strcmp(dir->current->name, ".") == 0 || strcmp(dir->current->name, "..") == 0)
|
||||
{
|
||||
memset(filestat, 0, sizeof(struct stat));
|
||||
filestat->st_mode = S_IFDIR;
|
||||
}
|
||||
else
|
||||
{
|
||||
ni = ntfsOpenEntry(dir->vd, dir->current->name);
|
||||
if (ni) {
|
||||
ntfsStat(dir->vd, ni, filestat);
|
||||
ntfsCloseEntry(dir->vd, ni);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Move to the next entry in the directory
|
||||
dir->current = dir->current->next;
|
||||
|
||||
// Update directory times
|
||||
ntfsUpdateTimes(dir->vd, dir->ni, NTFS_UPDATE_ATIME);
|
||||
|
||||
// Unlock
|
||||
ntfsUnlock(dir->vd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ntfs_dirclose_r (struct _reent *r, DIR_ITER *dirState)
|
||||
{
|
||||
ntfs_log_trace("dirState %p\n", dirState);
|
||||
|
||||
ntfs_dir_state* dir = STATE(dirState);
|
||||
|
||||
// Sanity check
|
||||
if (!dir || !dir->vd) {
|
||||
r->_errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Lock
|
||||
ntfsLock(dir->vd);
|
||||
|
||||
// Close the directory
|
||||
ntfsCloseDir(dir);
|
||||
|
||||
// Remove the directory from the double-linked FILO list of open directories
|
||||
dir->vd->openDirCount--;
|
||||
if (dir->nextOpenDir)
|
||||
dir->nextOpenDir->prevOpenDir = dir->prevOpenDir;
|
||||
if (dir->prevOpenDir)
|
||||
dir->prevOpenDir->nextOpenDir = dir->nextOpenDir;
|
||||
else
|
||||
dir->vd->firstOpenDir = dir->nextOpenDir;
|
||||
|
||||
// Unlock
|
||||
ntfsUnlock(dir->vd);
|
||||
|
||||
return 0;
|
||||
}
|
68
source/ntfsdir.h
Normal file
68
source/ntfsdir.h
Normal file
@ -0,0 +1,68 @@
|
||||
/**
|
||||
* ntfs_dir.c - devoptab directory routines for NTFS-based devices.
|
||||
*
|
||||
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
||||
* Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFSDIR_H
|
||||
#define _NTFSDIR_H
|
||||
|
||||
#include "ntfsinternal.h"
|
||||
#include <sys/reent.h>
|
||||
|
||||
/**
|
||||
* ntfs_dir_entry - Directory entry
|
||||
*/
|
||||
typedef struct _ntfs_dir_entry {
|
||||
char *name;
|
||||
u64 mref;
|
||||
struct _ntfs_dir_entry *next;
|
||||
} ntfs_dir_entry;
|
||||
|
||||
/**
|
||||
* ntfs_dir_state - Directory state
|
||||
*/
|
||||
typedef struct _ntfs_dir_state {
|
||||
ntfs_vd *vd; /* Volume this directory belongs to */
|
||||
ntfs_inode *ni; /* Directory descriptor */
|
||||
ntfs_dir_entry *first; /* The first entry in the directory */
|
||||
ntfs_dir_entry *current; /* The current entry in the directory */
|
||||
struct _ntfs_dir_state *prevOpenDir; /* The previous entry in a double-linked FILO list of open directories */
|
||||
struct _ntfs_dir_state *nextOpenDir; /* The next entry in a double-linked FILO list of open directories */
|
||||
} ntfs_dir_state;
|
||||
|
||||
/* Directory state routines */
|
||||
void ntfsCloseDir (ntfs_dir_state *file);
|
||||
|
||||
/* Gekko devoptab directory routines for NTFS-based devices */
|
||||
extern int ntfs_stat_r (struct _reent *r, const char *path, struct stat *st);
|
||||
extern int ntfs_link_r (struct _reent *r, const char *existing, const char *newLink);
|
||||
extern int ntfs_unlink_r (struct _reent *r, const char *name);
|
||||
extern int ntfs_chdir_r (struct _reent *r, const char *name);
|
||||
extern int ntfs_rename_r (struct _reent *r, const char *oldName, const char *newName);
|
||||
extern int ntfs_mkdir_r (struct _reent *r, const char *path, int mode);
|
||||
extern int ntfs_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf);
|
||||
|
||||
/* Gekko devoptab directory walking routines for NTFS-based devices */
|
||||
extern DIR_ITER *ntfs_diropen_r (struct _reent *r, DIR_ITER *dirState, const char *path);
|
||||
extern int ntfs_dirreset_r (struct _reent *r, DIR_ITER *dirState);
|
||||
extern int ntfs_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat);
|
||||
extern int ntfs_dirclose_r (struct _reent *r, DIR_ITER *dirState);
|
||||
|
||||
#endif /* _NTFSDIR_H */
|
||||
|
526
source/ntfsfile.c
Normal file
526
source/ntfsfile.c
Normal file
@ -0,0 +1,526 @@
|
||||
/**
|
||||
* ntfsfile.c - devoptab file routines for NTFS-based devices.
|
||||
*
|
||||
* Copyright (c) 2010 Dimok
|
||||
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
||||
* Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "ntfsinternal.h"
|
||||
#include "ntfsfile.h"
|
||||
|
||||
#define STATE(x) ((ntfs_file_state*)x)
|
||||
|
||||
void ntfsCloseFile (ntfs_file_state *file)
|
||||
{
|
||||
// Sanity check
|
||||
if (!file || !file->vd)
|
||||
return;
|
||||
|
||||
// Special case fix ups for compressed and/or encrypted files
|
||||
if (file->compressed)
|
||||
ntfs_attr_pclose(file->data_na);
|
||||
#ifdef HAVE_SETXATTR
|
||||
if (file->encrypted)
|
||||
ntfs_efs_fixup_attribute(NULL, file->data_na);
|
||||
#endif
|
||||
// Close the file data attribute (if open)
|
||||
if (file->data_na)
|
||||
ntfs_attr_close(file->data_na);
|
||||
|
||||
// Sync the file (and its attributes) to disc
|
||||
if(file->write)
|
||||
{
|
||||
ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME | NTFS_UPDATE_CTIME);
|
||||
ntfsSync(file->vd, file->ni);
|
||||
}
|
||||
|
||||
if (file->read)
|
||||
ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME);
|
||||
|
||||
// Close the file (if open)
|
||||
if (file->ni)
|
||||
ntfsCloseEntry(file->vd, file->ni);
|
||||
|
||||
// Reset the file state
|
||||
file->ni = NULL;
|
||||
file->data_na = NULL;
|
||||
file->flags = 0;
|
||||
file->read = false;
|
||||
file->write = false;
|
||||
file->append = false;
|
||||
file->pos = 0;
|
||||
file->len = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode)
|
||||
{
|
||||
ntfs_log_trace("fileStruct %p, path %s, flags %i, mode %i\n", fileStruct, path, flags, mode);
|
||||
|
||||
ntfs_file_state* file = STATE(fileStruct);
|
||||
|
||||
// Get the volume descriptor for this path
|
||||
file->vd = ntfsGetVolume(path);
|
||||
if (!file->vd) {
|
||||
r->_errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Lock
|
||||
ntfsLock(file->vd);
|
||||
|
||||
// Determine which mode the file is opened for
|
||||
file->flags = flags;
|
||||
if ((flags & 0x03) == O_RDONLY) {
|
||||
file->read = true;
|
||||
file->write = false;
|
||||
file->append = false;
|
||||
} else if ((flags & 0x03) == O_WRONLY) {
|
||||
file->read = false;
|
||||
file->write = true;
|
||||
file->append = (flags & O_APPEND);
|
||||
} else if ((flags & 0x03) == O_RDWR) {
|
||||
file->read = true;
|
||||
file->write = true;
|
||||
file->append = (flags & O_APPEND);
|
||||
} else {
|
||||
r->_errno = EACCES;
|
||||
ntfsUnlock(file->vd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Try and find the file and (if found) ensure that it is not a directory
|
||||
file->ni = ntfsOpenEntry(file->vd, path);
|
||||
if (file->ni && (file->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) {
|
||||
ntfsCloseEntry(file->vd, file->ni);
|
||||
ntfsUnlock(file->vd);
|
||||
r->_errno = EISDIR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Are we creating this file?
|
||||
if ((flags & O_CREAT) && !file->ni) {
|
||||
|
||||
// Create the file
|
||||
file->ni = ntfsCreate(file->vd, path, S_IFREG, NULL);
|
||||
if (!file->ni) {
|
||||
ntfsUnlock(file->vd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Sanity check, the file should be open by now
|
||||
if (!file->ni) {
|
||||
ntfsUnlock(file->vd);
|
||||
r->_errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Open the files data attribute
|
||||
file->data_na = ntfs_attr_open(file->ni, AT_DATA, AT_UNNAMED, 0);
|
||||
if(!file->data_na) {
|
||||
ntfsCloseEntry(file->vd, file->ni);
|
||||
ntfsUnlock(file->vd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Determine if this files data is compressed and/or encrypted
|
||||
file->compressed = NAttrCompressed(file->data_na) || (file->ni->flags & FILE_ATTR_COMPRESSED);
|
||||
file->encrypted = NAttrEncrypted(file->data_na) || (file->ni->flags & FILE_ATTR_ENCRYPTED);
|
||||
|
||||
// We cannot read/write encrypted files
|
||||
if (file->encrypted) {
|
||||
ntfs_attr_close(file->data_na);
|
||||
ntfsCloseEntry(file->vd, file->ni);
|
||||
ntfsUnlock(file->vd);
|
||||
r->_errno = EACCES;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Make sure we aren't trying to write to a read-only file
|
||||
if ((file->ni->flags & FILE_ATTR_READONLY) && file->write) {
|
||||
ntfs_attr_close(file->data_na);
|
||||
ntfsCloseEntry(file->vd, file->ni);
|
||||
ntfsUnlock(file->vd);
|
||||
r->_errno = EROFS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Truncate the file if requested
|
||||
if ((flags & O_TRUNC) && file->write) {
|
||||
if (ntfs_attr_truncate(file->data_na, 0)) {
|
||||
ntfs_attr_close(file->data_na);
|
||||
ntfsCloseEntry(file->vd, file->ni);
|
||||
ntfsUnlock(file->vd);
|
||||
r->_errno = errno;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the files current position and length
|
||||
file->pos = 0;
|
||||
file->len = file->data_na->data_size;
|
||||
|
||||
ntfs_log_trace("file->len %d\n", file->len);
|
||||
|
||||
// Update file times
|
||||
ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME);
|
||||
|
||||
// Insert the file into the double-linked FILO list of open files
|
||||
if (file->vd->firstOpenFile) {
|
||||
file->nextOpenFile = file->vd->firstOpenFile;
|
||||
file->vd->firstOpenFile->prevOpenFile = file;
|
||||
} else {
|
||||
file->nextOpenFile = NULL;
|
||||
}
|
||||
file->prevOpenFile = NULL;
|
||||
file->vd->firstOpenFile = file;
|
||||
file->vd->openFileCount++;
|
||||
|
||||
// Unlock
|
||||
ntfsUnlock(file->vd);
|
||||
|
||||
return (int)fileStruct;
|
||||
}
|
||||
|
||||
int ntfs_close_r (struct _reent *r, int fd)
|
||||
{
|
||||
ntfs_log_trace("fd %p\n", fd);
|
||||
|
||||
ntfs_file_state* file = STATE(fd);
|
||||
|
||||
// Sanity check
|
||||
if (!file || !file->vd) {
|
||||
r->_errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Lock
|
||||
ntfsLock(file->vd);
|
||||
|
||||
// Close the file
|
||||
ntfsCloseFile(file);
|
||||
|
||||
// Remove the file from the double-linked FILO list of open files
|
||||
file->vd->openFileCount--;
|
||||
if (file->nextOpenFile)
|
||||
file->nextOpenFile->prevOpenFile = file->prevOpenFile;
|
||||
if (file->prevOpenFile)
|
||||
file->prevOpenFile->nextOpenFile = file->nextOpenFile;
|
||||
else
|
||||
file->vd->firstOpenFile = file->nextOpenFile;
|
||||
|
||||
// Unlock
|
||||
ntfsUnlock(file->vd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t ntfs_write_r (struct _reent *r, int fd, const char *ptr, size_t len)
|
||||
{
|
||||
ntfs_log_trace("fd %p, ptr %p, len %Li\n", fd, ptr, len);
|
||||
|
||||
ntfs_file_state* file = STATE(fd);
|
||||
ssize_t written = 0;
|
||||
off_t old_pos = 0;
|
||||
|
||||
// Sanity check
|
||||
if (!file || !file->vd || !file->ni || !file->data_na) {
|
||||
r->_errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Short circuit cases where we don't actually have to do anything
|
||||
if (!ptr || len <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Lock
|
||||
ntfsLock(file->vd);
|
||||
|
||||
// Check that we are allowed to write to this file
|
||||
if (!file->write) {
|
||||
ntfsUnlock(file->vd);
|
||||
r->_errno = EACCES;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// If we are in append mode, backup the current position and move to the end of the file
|
||||
if (file->append) {
|
||||
old_pos = file->pos;
|
||||
file->pos = file->len;
|
||||
}
|
||||
|
||||
// Write to the files data atrribute
|
||||
while (len) {
|
||||
ssize_t ret = ntfs_attr_pwrite(file->data_na, file->pos, len, ptr);
|
||||
if (ret <= 0) {
|
||||
ntfsUnlock(file->vd);
|
||||
r->_errno = errno;
|
||||
return -1;
|
||||
}
|
||||
len -= ret;
|
||||
file->pos += ret;
|
||||
written += ret;
|
||||
}
|
||||
|
||||
// If we are in append mode, restore the current position to were it was prior to this write
|
||||
if (file->append) {
|
||||
file->pos = old_pos;
|
||||
}
|
||||
|
||||
// Mark the file for archiving (if we actually wrote something)
|
||||
if (written)
|
||||
file->ni->flags |= FILE_ATTR_ARCHIVE;
|
||||
|
||||
// Update the files data length
|
||||
file->len = file->data_na->data_size;
|
||||
|
||||
// Unlock
|
||||
ntfsUnlock(file->vd);
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
ssize_t ntfs_read_r (struct _reent *r, int fd, char *ptr, size_t len)
|
||||
{
|
||||
ntfs_log_trace("fd %p, ptr %p, len %Li\n", fd, ptr, len);
|
||||
|
||||
ntfs_file_state* file = STATE(fd);
|
||||
ssize_t read = 0;
|
||||
|
||||
// Sanity check
|
||||
if (!file || !file->vd || !file->ni || !file->data_na) {
|
||||
r->_errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Short circuit cases where we don't actually have to do anything
|
||||
if (!ptr || len <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Lock
|
||||
ntfsLock(file->vd);
|
||||
|
||||
// Check that we are allowed to read from this file
|
||||
if (!file->read) {
|
||||
ntfsUnlock(file->vd);
|
||||
r->_errno = EACCES;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Don't read past the end of file
|
||||
if (file->pos + len > file->len) {
|
||||
r->_errno = EOVERFLOW;
|
||||
len = file->len - file->pos;
|
||||
ntfs_log_trace("EOVERFLOW");
|
||||
}
|
||||
|
||||
ntfs_log_trace("file->pos:%d, len:%d, file->len:%d \n", (u32)file->pos, (u32)len, (u32)file->len);
|
||||
|
||||
// Read from the files data attribute
|
||||
while (len) {
|
||||
ssize_t ret = ntfs_attr_pread(file->data_na, file->pos, len, ptr);
|
||||
if (ret <= 0 || ret > len) {
|
||||
ntfsUnlock(file->vd);
|
||||
r->_errno = errno;
|
||||
return -1;
|
||||
}
|
||||
ptr += ret;
|
||||
len -= ret;
|
||||
file->pos += ret;
|
||||
read += ret;
|
||||
}
|
||||
//ntfs_log_trace("file->pos: %d \n", (u32)file->pos);
|
||||
// Update file times (if we actually read something)
|
||||
|
||||
// Unlock
|
||||
ntfsUnlock(file->vd);
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
off_t ntfs_seek_r (struct _reent *r, int fd, off_t pos, int dir)
|
||||
{
|
||||
ntfs_log_trace("fd %p, pos %Li, dir %i\n", fd, pos, dir);
|
||||
|
||||
ntfs_file_state* file = STATE(fd);
|
||||
off_t position = 0;
|
||||
|
||||
// Sanity check
|
||||
if (!file || !file->vd || !file->ni || !file->data_na) {
|
||||
r->_errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Lock
|
||||
ntfsLock(file->vd);
|
||||
|
||||
// Set the files current position
|
||||
switch(dir) {
|
||||
case SEEK_SET: position = file->pos = MIN(MAX(pos, 0), file->len); break;
|
||||
case SEEK_CUR: position = file->pos = MIN(MAX(file->pos + pos, 0), file->len); break;
|
||||
case SEEK_END: position = file->pos = MIN(MAX(file->len + pos, 0), file->len); break;
|
||||
}
|
||||
|
||||
// Unlock
|
||||
ntfsUnlock(file->vd);
|
||||
|
||||
return position;
|
||||
}
|
||||
int ntfs_fstat_r (struct _reent *r, int fd, struct stat *st)
|
||||
{
|
||||
ntfs_log_trace("fd %p\n", fd);
|
||||
|
||||
ntfs_file_state* file = STATE(fd);
|
||||
int ret = 0;
|
||||
|
||||
// Sanity check
|
||||
if (!file || !file->vd || !file->ni || !file->data_na) {
|
||||
r->_errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Short circuit cases were we don't actually have to do anything
|
||||
if (!st)
|
||||
return 0;
|
||||
|
||||
// Get the file stats
|
||||
ret = ntfsStat(file->vd, file->ni, st);
|
||||
if (ret)
|
||||
r->_errno = errno;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ntfs_ftruncate_r (struct _reent *r, int fd, off_t len)
|
||||
{
|
||||
ntfs_log_trace("fd %p, len %Li\n", fd, len);
|
||||
|
||||
ntfs_file_state* file = STATE(fd);
|
||||
|
||||
// Sanity check
|
||||
if (!file || !file->vd || !file->ni || !file->data_na) {
|
||||
r->_errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Lock
|
||||
ntfsLock(file->vd);
|
||||
|
||||
// Check that we are allowed to write to this file
|
||||
if (!file->write) {
|
||||
ntfsUnlock(file->vd);
|
||||
r->_errno = EACCES;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// For compressed files, only deleting and expanding contents are implemented
|
||||
if (file->compressed &&
|
||||
len > 0 &&
|
||||
len < file->data_na->initialized_size) {
|
||||
ntfsUnlock(file->vd);
|
||||
r->_errno = EOPNOTSUPP;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Resize the files data attribute, either by expanding or truncating
|
||||
if (file->compressed && len > file->data_na->initialized_size) {
|
||||
char zero = 0;
|
||||
if (ntfs_attr_pwrite(file->data_na, len - 1, 1, &zero) <= 0) {
|
||||
ntfsUnlock(file->vd);
|
||||
r->_errno = errno;
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (ntfs_attr_truncate(file->data_na, len)) {
|
||||
ntfsUnlock(file->vd);
|
||||
r->_errno = errno;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the file for archiving (if we actually changed something)
|
||||
if (file->len != file->data_na->data_size)
|
||||
file->ni->flags |= FILE_ATTR_ARCHIVE;
|
||||
|
||||
// Update file times (if we actually changed something)
|
||||
if (file->len != file->data_na->data_size)
|
||||
ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_AMCTIME);
|
||||
|
||||
// Update the files data length
|
||||
file->len = file->data_na->data_size;
|
||||
|
||||
// Sync the file (and its attributes) to disc
|
||||
ntfsSync(file->vd, file->ni);
|
||||
|
||||
// Unlock
|
||||
ntfsUnlock(file->vd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ntfs_fsync_r (struct _reent *r, int fd)
|
||||
{
|
||||
ntfs_log_trace("fd %p\n", fd);
|
||||
|
||||
ntfs_file_state* file = STATE(fd);
|
||||
int ret = 0;
|
||||
|
||||
// Sanity check
|
||||
if (!file || !file->vd || !file->ni || !file->data_na) {
|
||||
r->_errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Lock
|
||||
ntfsLock(file->vd);
|
||||
|
||||
// Sync the file (and its attributes) to disc
|
||||
ret = ntfsSync(file->vd, file->ni);
|
||||
if (ret)
|
||||
r->_errno = errno;
|
||||
|
||||
// Unlock
|
||||
ntfsUnlock(file->vd);
|
||||
|
||||
return ret;
|
||||
}
|
65
source/ntfsfile.h
Normal file
65
source/ntfsfile.h
Normal file
@ -0,0 +1,65 @@
|
||||
/**
|
||||
* ntfsfile.c - devoptab file routines for NTFS-based devices.
|
||||
*
|
||||
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
||||
* Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFSFILE_H
|
||||
#define _NTFSFILE_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "ntfsinternal.h"
|
||||
#include <sys/reent.h>
|
||||
|
||||
/**
|
||||
* ntfs_file_state - File state
|
||||
*/
|
||||
typedef struct _ntfs_file_state {
|
||||
ntfs_vd *vd; /* Volume this file belongs to */
|
||||
ntfs_inode *ni; /* File descriptor */
|
||||
ntfs_attr *data_na; /* File data descriptor */
|
||||
int flags; /* Opening flags */
|
||||
bool read; /* True if allowed to read from file */
|
||||
bool write; /* True if allowed to write to file */
|
||||
bool append; /* True if allowed to append to file */
|
||||
bool compressed; /* True if file data is compressed */
|
||||
bool encrypted; /* True if file data is encryted */
|
||||
off_t pos; /* Current position within the file (in bytes) */
|
||||
u64 len; /* Total length of the file (in bytes) */
|
||||
struct _ntfs_file_state *prevOpenFile; /* The previous entry in a double-linked FILO list of open files */
|
||||
struct _ntfs_file_state *nextOpenFile; /* The next entry in a double-linked FILO list of open files */
|
||||
} ntfs_file_state;
|
||||
|
||||
/* File state routines */
|
||||
void ntfsCloseFile (ntfs_file_state *file);
|
||||
|
||||
/* Gekko devoptab file routines for NTFS-based devices */
|
||||
extern int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode);
|
||||
extern int ntfs_close_r (struct _reent *r, int fd);
|
||||
extern ssize_t ntfs_write_r (struct _reent *r, int fd, const char *ptr, size_t len);
|
||||
extern ssize_t ntfs_read_r (struct _reent *r, int fd, char *ptr, size_t len);
|
||||
extern off_t ntfs_seek_r (struct _reent *r, int fd, off_t pos, int dir);
|
||||
extern int ntfs_fstat_r (struct _reent *r, int fd, struct stat *st);
|
||||
extern int ntfs_ftruncate_r (struct _reent *r, int fd, off_t len);
|
||||
extern int ntfs_fsync_r (struct _reent *r, int fd);
|
||||
|
||||
#endif /* _NTFSFILE_H */
|
||||
|
868
source/ntfsinternal.c
Normal file
868
source/ntfsinternal.c
Normal file
@ -0,0 +1,868 @@
|
||||
/**
|
||||
* ntfsinternal.h - Internal support routines for NTFS-based devices.
|
||||
*
|
||||
* Copyright (c) 2010 Dimok
|
||||
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
||||
* Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "ntfsinternal.h"
|
||||
#include "ntfsdir.h"
|
||||
#include "ntfsfile.h"
|
||||
|
||||
#if defined(__wii__)
|
||||
#include <sdcard/wiisd_io.h>
|
||||
#include <sdcard/gcsd.h>
|
||||
#include <ogc/usbstorage.h>
|
||||
|
||||
const INTERFACE_ID ntfs_disc_interfaces[] = {
|
||||
{ "sd", &__io_wiisd },
|
||||
{ "usb", &__io_usbstorage },
|
||||
{ "carda", &__io_gcsda },
|
||||
{ "cardb", &__io_gcsdb },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
#elif defined(__gamecube__)
|
||||
#include <sdcard/gcsd.h>
|
||||
|
||||
const INTERFACE_ID ntfs_disc_interfaces[] = {
|
||||
{ "carda", &__io_gcsda },
|
||||
{ "cardb", &__io_gcsdb },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
int ntfsAddDevice (const char *name, void *deviceData)
|
||||
{
|
||||
const devoptab_t *devoptab_ntfs = ntfsGetDevOpTab();
|
||||
devoptab_t *dev = NULL;
|
||||
char *devname = NULL;
|
||||
int i;
|
||||
|
||||
// Sanity check
|
||||
if (!name || !deviceData || !devoptab_ntfs) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Allocate a devoptab for this device
|
||||
dev = (devoptab_t *) ntfs_alloc(sizeof(devoptab_t) + strlen(name) + 1);
|
||||
if (!dev) {
|
||||
errno = ENOMEM;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use the space allocated at the end of the devoptab for storing the device name
|
||||
devname = (char*)(dev + 1);
|
||||
strcpy(devname, name);
|
||||
|
||||
// Setup the devoptab
|
||||
memcpy(dev, devoptab_ntfs, sizeof(devoptab_t));
|
||||
dev->name = devname;
|
||||
dev->deviceData = deviceData;
|
||||
|
||||
// Add the device to the devoptab table (if there is a free slot)
|
||||
for (i = 0; i < STD_MAX; i++) {
|
||||
if (devoptab_list[i] == devoptab_list[0] && i != 0) {
|
||||
devoptab_list[i] = dev;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// If we reach here then there are no free slots in the devoptab table for this device
|
||||
errno = EADDRNOTAVAIL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void ntfsRemoveDevice (const char *path)
|
||||
{
|
||||
const devoptab_t *devoptab = NULL;
|
||||
char name[128] = {0};
|
||||
int i;
|
||||
|
||||
// Get the device name from the path
|
||||
strncpy(name, path, 127);
|
||||
strtok(name, ":/");
|
||||
|
||||
// Find and remove the specified device from the devoptab table
|
||||
// NOTE: We do this manually due to a 'bug' in RemoveDevice
|
||||
// which ignores names with suffixes and causes names
|
||||
// like "ntfs" and "ntfs1" to be seen as equals
|
||||
for (i = 0; i < STD_MAX; i++) {
|
||||
devoptab = devoptab_list[i];
|
||||
if (devoptab && devoptab->name) {
|
||||
if (strcmp(name, devoptab->name) == 0) {
|
||||
devoptab_list[i] = devoptab_list[0];
|
||||
ntfs_free((devoptab_t*)devoptab);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const devoptab_t *ntfsGetDevice (const char *path, bool useDefaultDevice)
|
||||
{
|
||||
const devoptab_t *devoptab = NULL;
|
||||
char name[128] = {0};
|
||||
int i;
|
||||
|
||||
// Get the device name from the path
|
||||
strncpy(name, path, 127);
|
||||
strtok(name, ":/");
|
||||
|
||||
// Search the devoptab table for the specified device name
|
||||
// NOTE: We do this manually due to a 'bug' in GetDeviceOpTab
|
||||
// which ignores names with suffixes and causes names
|
||||
// like "ntfs" and "ntfs1" to be seen as equals
|
||||
for (i = 0; i < STD_MAX; i++) {
|
||||
devoptab = devoptab_list[i];
|
||||
if (devoptab && devoptab->name) {
|
||||
if (strcmp(name, devoptab->name) == 0) {
|
||||
return devoptab;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we reach here then we couldn't find the device name,
|
||||
// chances are that this path has no device name in it.
|
||||
// Call GetDeviceOpTab to get our default device (chdir).
|
||||
if (useDefaultDevice)
|
||||
return GetDeviceOpTab("");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const INTERFACE_ID *ntfsGetDiscInterfaces (void)
|
||||
{
|
||||
// Get all know disc interfaces on the host system
|
||||
return ntfs_disc_interfaces;
|
||||
}
|
||||
|
||||
ntfs_vd *ntfsGetVolume (const char *path)
|
||||
{
|
||||
// Get the volume descriptor from the paths associated devoptab (if found)
|
||||
const devoptab_t *devoptab_ntfs = ntfsGetDevOpTab();
|
||||
const devoptab_t *devoptab = ntfsGetDevice(path, true);
|
||||
if (devoptab && devoptab_ntfs && (devoptab->open_r == devoptab_ntfs->open_r))
|
||||
return (ntfs_vd*)devoptab->deviceData;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int ntfsInitVolume (ntfs_vd *vd)
|
||||
{
|
||||
// Sanity check
|
||||
if (!vd) {
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Initialise the volume lock
|
||||
LWP_MutexInit(&vd->lock, false);
|
||||
|
||||
// Reset the volumes name cache
|
||||
vd->name[0] = '\0';
|
||||
|
||||
// Reset the volumes current directory
|
||||
vd->cwd_ni = NULL;
|
||||
|
||||
// Reset open directory and file stats
|
||||
vd->openDirCount = 0;
|
||||
vd->openFileCount = 0;
|
||||
vd->firstOpenDir = NULL;
|
||||
vd->firstOpenFile = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ntfsDeinitVolume (ntfs_vd *vd)
|
||||
{
|
||||
// Sanity check
|
||||
if (!vd) {
|
||||
errno = ENODEV;
|
||||
return;
|
||||
}
|
||||
|
||||
// Lock
|
||||
ntfsLock(vd);
|
||||
|
||||
// Close any directories which are still open (lazy programmers!)
|
||||
ntfs_dir_state *nextDir = vd->firstOpenDir;
|
||||
while (nextDir) {
|
||||
ntfs_log_warning("Cleaning up orphaned directory @ %p\n", nextDir);
|
||||
ntfsCloseDir(nextDir);
|
||||
nextDir = nextDir->nextOpenDir;
|
||||
}
|
||||
|
||||
// Close any files which are still open (lazy programmers!)
|
||||
ntfs_file_state *nextFile = vd->firstOpenFile;
|
||||
while (nextFile) {
|
||||
ntfs_log_warning("Cleaning up orphaned file @ %p\n", nextFile);
|
||||
ntfsCloseFile(nextFile);
|
||||
nextFile = nextFile->nextOpenFile;
|
||||
}
|
||||
|
||||
// Reset open directory and file stats
|
||||
vd->openDirCount = 0;
|
||||
vd->openFileCount = 0;
|
||||
vd->firstOpenDir = NULL;
|
||||
vd->firstOpenFile = NULL;
|
||||
|
||||
// Close the volumes current directory (if any)
|
||||
//if (vd->cwd_ni) {
|
||||
//ntfsCloseEntry(vd, vd->cwd_ni);
|
||||
//vd->cwd_ni = NULL;
|
||||
//}
|
||||
|
||||
// Force the underlying device to sync
|
||||
vd->dev->d_ops->sync(vd->dev);
|
||||
|
||||
// Unlock
|
||||
ntfsUnlock(vd);
|
||||
|
||||
// Deinitialise the volume lock
|
||||
LWP_MutexDestroy(vd->lock);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ntfs_inode *ntfsOpenEntry (ntfs_vd *vd, const char *path)
|
||||
{
|
||||
return ntfsParseEntry(vd, path, 1);
|
||||
}
|
||||
|
||||
ntfs_inode *ntfsParseEntry (ntfs_vd *vd, const char *path, int reparseLevel)
|
||||
{
|
||||
ntfs_inode *ni = NULL;
|
||||
char *target = NULL;
|
||||
int attr_size;
|
||||
|
||||
// Sanity check
|
||||
if (!vd) {
|
||||
errno = ENODEV;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get the actual path of the entry
|
||||
path = ntfsRealPath(path);
|
||||
if (!path) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
} else if (path[0] == '\0') {
|
||||
path = ".";
|
||||
}
|
||||
|
||||
// Find the entry, taking into account our current directory (if any)
|
||||
if (path[0] != PATH_SEP)
|
||||
ni = ntfs_pathname_to_inode(vd->vol, vd->cwd_ni, path++);
|
||||
else
|
||||
ni = ntfs_pathname_to_inode(vd->vol, NULL, path);
|
||||
|
||||
// If the entry was found and it has reparse data then parse its true path;
|
||||
// this resolves the true location of symbolic links and directory junctions
|
||||
if (ni && (ni->flags & FILE_ATTR_REPARSE_POINT)) {
|
||||
if (ntfs_possible_symlink(ni)) {
|
||||
|
||||
// Sanity check, give up if we are parsing to deep
|
||||
if (reparseLevel > NTFS_MAX_SYMLINK_DEPTH) {
|
||||
ntfsCloseEntry(vd, ni);
|
||||
errno = ELOOP;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get the target path of this entry
|
||||
target = ntfs_make_symlink(ni, path, &attr_size);
|
||||
if (!target) {
|
||||
ntfsCloseEntry(vd, ni);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Close the entry (we are no longer interested in it)
|
||||
ntfsCloseEntry(vd, ni);
|
||||
|
||||
// Parse the entries target
|
||||
ni = ntfsParseEntry(vd, target, reparseLevel++);
|
||||
|
||||
// Clean up
|
||||
// use free because the value was not allocated with ntfs_alloc
|
||||
free(target);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return ni;
|
||||
}
|
||||
|
||||
void ntfsCloseEntry (ntfs_vd *vd, ntfs_inode *ni)
|
||||
{
|
||||
// Sanity check
|
||||
if (!vd) {
|
||||
errno = ENODEV;
|
||||
return;
|
||||
}
|
||||
|
||||
// Lock
|
||||
ntfsLock(vd);
|
||||
|
||||
// Sync the entry (if it is dirty)
|
||||
if (NInoDirty(ni))
|
||||
ntfsSync(vd, ni);
|
||||
|
||||
// Close the entry
|
||||
ntfs_inode_close(ni);
|
||||
|
||||
// Unlock
|
||||
ntfsUnlock(vd);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
ntfs_inode *ntfsCreate (ntfs_vd *vd, const char *path, mode_t type, const char *target)
|
||||
{
|
||||
ntfs_inode *dir_ni = NULL, *ni = NULL;
|
||||
char *dir = NULL;
|
||||
char *name = NULL;
|
||||
ntfschar *uname = NULL, *utarget = NULL;
|
||||
int uname_len, utarget_len;
|
||||
|
||||
// Sanity check
|
||||
if (!vd) {
|
||||
errno = ENODEV;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// You cannot link between devices
|
||||
if(target) {
|
||||
if(vd != ntfsGetVolume(target)) {
|
||||
errno = EXDEV;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the actual paths of the entry
|
||||
path = ntfsRealPath(path);
|
||||
target = ntfsRealPath(target);
|
||||
if (!path) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Lock
|
||||
ntfsLock(vd);
|
||||
|
||||
// Get the unicode name for the entry and find its parent directory
|
||||
// TODO: This looks horrible, clean it up
|
||||
dir = strdup(path);
|
||||
if (!dir) {
|
||||
errno = EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
name = strrchr(dir, '/');
|
||||
if (name)
|
||||
name++;
|
||||
else
|
||||
name = dir;
|
||||
uname_len = ntfsLocalToUnicode(name, &uname);
|
||||
if (uname_len < 0) {
|
||||
errno = EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
name = strrchr(dir, '/');
|
||||
if(name)
|
||||
{
|
||||
name++;
|
||||
name[0] = 0;
|
||||
}
|
||||
|
||||
// Open the entries parent directory
|
||||
dir_ni = ntfsOpenEntry(vd, dir);
|
||||
if (!dir_ni) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Create the entry
|
||||
switch (type) {
|
||||
|
||||
// Symbolic link
|
||||
case S_IFLNK:
|
||||
if (!target) {
|
||||
errno = EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
utarget_len = ntfsLocalToUnicode(target, &utarget);
|
||||
if (utarget_len < 0) {
|
||||
errno = EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
ni = ntfs_create_symlink(dir_ni, 0, uname, uname_len, utarget, utarget_len);
|
||||
break;
|
||||
|
||||
// Directory or file
|
||||
case S_IFDIR:
|
||||
case S_IFREG:
|
||||
ni = ntfs_create(dir_ni, 0, uname, uname_len, type);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
// If the entry was created
|
||||
if (ni) {
|
||||
|
||||
// Mark the entry for archiving
|
||||
ni->flags |= FILE_ATTR_ARCHIVE;
|
||||
|
||||
// Mark the entry as dirty
|
||||
NInoSetDirty(ni);
|
||||
|
||||
// Sync the entry to disc
|
||||
ntfsSync(vd, ni);
|
||||
|
||||
// Update parent directories times
|
||||
ntfsUpdateTimes(vd, dir_ni, NTFS_UPDATE_MCTIME);
|
||||
|
||||
}
|
||||
|
||||
cleanup:
|
||||
|
||||
if(dir_ni)
|
||||
ntfsCloseEntry(vd, dir_ni);
|
||||
|
||||
// use free because the value was not allocated with ntfs_alloc
|
||||
if(utarget)
|
||||
free(utarget);
|
||||
|
||||
if(uname)
|
||||
free(uname);
|
||||
|
||||
if(dir)
|
||||
ntfs_free(dir);
|
||||
|
||||
// Unlock
|
||||
ntfsUnlock(vd);
|
||||
|
||||
return ni;
|
||||
}
|
||||
|
||||
int ntfsLink (ntfs_vd *vd, const char *old_path, const char *new_path)
|
||||
{
|
||||
ntfs_inode *dir_ni = NULL, *ni = NULL;
|
||||
char *dir = NULL;
|
||||
char *name = NULL;
|
||||
ntfschar *uname = NULL;
|
||||
int uname_len;
|
||||
int res = 0;
|
||||
|
||||
// Sanity check
|
||||
if (!vd) {
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// You cannot link between devices
|
||||
if(vd != ntfsGetVolume(new_path)) {
|
||||
errno = EXDEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get the actual paths of the entry
|
||||
old_path = ntfsRealPath(old_path);
|
||||
new_path = ntfsRealPath(new_path);
|
||||
if (!old_path || !new_path) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Lock
|
||||
ntfsLock(vd);
|
||||
|
||||
// Get the unicode name for the entry and find its parent directory
|
||||
// TODO: This looks horrible, clean it up
|
||||
dir = strdup(new_path);
|
||||
if (!dir) {
|
||||
errno = EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
name = strrchr(dir, '/');
|
||||
if (name)
|
||||
name++;
|
||||
else
|
||||
name = dir;
|
||||
uname_len = ntfsLocalToUnicode(name, &uname);
|
||||
if (uname_len < 0) {
|
||||
errno = EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
*name = 0;
|
||||
|
||||
// Find the entry
|
||||
ni = ntfsOpenEntry(vd, old_path);
|
||||
if (!ni) {
|
||||
errno = ENOENT;
|
||||
res = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Open the entries new parent directory
|
||||
dir_ni = ntfsOpenEntry(vd, dir);
|
||||
if (!dir_ni) {
|
||||
errno = ENOENT;
|
||||
res = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Link the entry to its new parent
|
||||
if (ntfs_link(ni, dir_ni, uname, uname_len)) {
|
||||
res = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Update entry times
|
||||
ntfsUpdateTimes(vd, dir_ni, NTFS_UPDATE_MCTIME);
|
||||
|
||||
// Sync the entry to disc
|
||||
ntfsSync(vd, ni);
|
||||
|
||||
cleanup:
|
||||
|
||||
if(dir_ni)
|
||||
ntfsCloseEntry(vd, dir_ni);
|
||||
|
||||
if(ni)
|
||||
ntfsCloseEntry(vd, ni);
|
||||
|
||||
// use free because the value was not allocated with ntfs_alloc
|
||||
if(uname)
|
||||
free(uname);
|
||||
|
||||
if(dir)
|
||||
ntfs_free(dir);
|
||||
|
||||
// Unlock
|
||||
ntfsUnlock(vd);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int ntfsUnlink (ntfs_vd *vd, const char *path)
|
||||
{
|
||||
ntfs_inode *dir_ni = NULL, *ni = NULL;
|
||||
char *dir = NULL;
|
||||
char *name = NULL;
|
||||
ntfschar *uname = NULL;
|
||||
int uname_len;
|
||||
int res = 0;
|
||||
|
||||
// Sanity check
|
||||
if (!vd) {
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get the actual path of the entry
|
||||
path = ntfsRealPath(path);
|
||||
if (!path) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Lock
|
||||
ntfsLock(vd);
|
||||
|
||||
// Get the unicode name for the entry and find its parent directory
|
||||
// TODO: This looks horrible
|
||||
dir = strdup(path);
|
||||
if (!dir) {
|
||||
errno = EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
name = strrchr(dir, '/');
|
||||
if (name)
|
||||
name++;
|
||||
else
|
||||
name = dir;
|
||||
uname_len = ntfsLocalToUnicode(name, &uname);
|
||||
if (uname_len < 0) {
|
||||
errno = EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
name = strrchr(dir, '/');
|
||||
if(name)
|
||||
{
|
||||
name++;
|
||||
name[0] = 0;
|
||||
}
|
||||
|
||||
// Find the entry
|
||||
ni = ntfsOpenEntry(vd, path);
|
||||
if (!ni) {
|
||||
errno = ENOENT;
|
||||
res = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Open the entries parent directory
|
||||
dir_ni = ntfsOpenEntry(vd, dir);
|
||||
if (!dir_ni) {
|
||||
errno = ENOENT;
|
||||
res = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Unlink the entry from its parent
|
||||
if (ntfs_delete(vd->vol, path, ni, dir_ni, uname, uname_len)) {
|
||||
res = -1;
|
||||
}
|
||||
|
||||
// Force the underlying device to sync
|
||||
vd->dev->d_ops->sync(vd->dev);
|
||||
|
||||
// ntfs_delete() ALWAYS closes ni and dir_ni; so no need for us to anymore
|
||||
dir_ni = ni = NULL;
|
||||
|
||||
cleanup:
|
||||
|
||||
if(dir_ni)
|
||||
ntfsCloseEntry(vd, dir_ni);
|
||||
|
||||
if(ni)
|
||||
ntfsCloseEntry(vd, ni);
|
||||
|
||||
// use free because the value was not allocated with ntfs_alloc
|
||||
if(uname)
|
||||
free(uname);
|
||||
|
||||
if(dir)
|
||||
ntfs_free(dir);
|
||||
|
||||
// Unlock
|
||||
ntfsUnlock(vd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ntfsSync (ntfs_vd *vd, ntfs_inode *ni)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
// Sanity check
|
||||
if (!vd) {
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Sanity check
|
||||
if (!ni) {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Lock
|
||||
ntfsLock(vd);
|
||||
|
||||
// Sync the entry
|
||||
res = ntfs_inode_sync(ni);
|
||||
|
||||
// Force the underlying device to sync
|
||||
vd->dev->d_ops->sync(vd->dev);
|
||||
|
||||
// Unlock
|
||||
ntfsUnlock(vd);
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
int ntfsStat (ntfs_vd *vd, ntfs_inode *ni, struct stat *st)
|
||||
{
|
||||
ntfs_attr *na = NULL;
|
||||
int res = 0;
|
||||
|
||||
// Sanity check
|
||||
if (!vd) {
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Sanity check
|
||||
if (!ni) {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Short circuit cases were we don't actually have to do anything
|
||||
if (!st)
|
||||
return 0;
|
||||
|
||||
// Lock
|
||||
ntfsLock(vd);
|
||||
|
||||
// Zero out the stat buffer
|
||||
memset(st, 0, sizeof(struct stat));
|
||||
|
||||
// Is this entry a directory
|
||||
if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
|
||||
st->st_mode = S_IFDIR | (0777 & ~vd->dmask);
|
||||
st->st_nlink = 1;
|
||||
|
||||
// Open the directories index allocation table attribute
|
||||
na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4);
|
||||
if (na) {
|
||||
st->st_size = na->data_size;
|
||||
st->st_blocks = na->allocated_size >> 9;
|
||||
ntfs_attr_close(na);
|
||||
}
|
||||
|
||||
// Else it must be a file
|
||||
} else {
|
||||
st->st_mode = S_IFREG | (0777 & ~vd->fmask);
|
||||
st->st_size = ni->data_size;
|
||||
st->st_blocks = (ni->allocated_size + 511) >> 9;
|
||||
st->st_nlink = le16_to_cpu(ni->mrec->link_count);
|
||||
}
|
||||
|
||||
// Fill in the generic entry stats
|
||||
st->st_dev = vd->id;
|
||||
st->st_uid = vd->uid;
|
||||
st->st_gid = vd->gid;
|
||||
st->st_ino = ni->mft_no;
|
||||
st->st_atime = ni->last_access_time;
|
||||
st->st_ctime = ni->last_mft_change_time;
|
||||
st->st_mtime = ni->last_data_change_time;
|
||||
|
||||
// Update entry times
|
||||
ntfsUpdateTimes(vd, ni, NTFS_UPDATE_ATIME);
|
||||
|
||||
// Unlock
|
||||
ntfsUnlock(vd);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void ntfsUpdateTimes (ntfs_vd *vd, ntfs_inode *ni, ntfs_time_update_flags mask)
|
||||
{
|
||||
// Run the access time update strategy against the device driver settings first
|
||||
if (vd && vd->atime == ATIME_DISABLED)
|
||||
mask &= ~NTFS_UPDATE_ATIME;
|
||||
|
||||
// Update entry times
|
||||
if (ni && mask)
|
||||
ntfs_inode_update_times(ni, mask);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const char *ntfsRealPath (const char *path)
|
||||
{
|
||||
// Sanity check
|
||||
if (!path)
|
||||
return NULL;
|
||||
|
||||
// Move the path pointer to the start of the actual path
|
||||
if (strchr(path, ':') != NULL) {
|
||||
path = strchr(path, ':') + 1;
|
||||
}
|
||||
if (strchr(path, ':') != NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
int ntfsUnicodeToLocal (const ntfschar *ins, const int ins_len, char **outs, int outs_len)
|
||||
{
|
||||
int len = 0;
|
||||
int i;
|
||||
|
||||
// Sanity check
|
||||
if (!ins || !ins_len || !outs)
|
||||
return 0;
|
||||
|
||||
char * ucstombs_out = NULL;
|
||||
|
||||
// Convert the unicode string to our current local
|
||||
len = ntfs_ucstombs(ins, ins_len, &ucstombs_out, outs_len);
|
||||
|
||||
if(ucstombs_out)
|
||||
{
|
||||
//use proper allocation
|
||||
*outs = (char *) ntfs_alloc(strlen(ucstombs_out) + 1);
|
||||
if(!*outs)
|
||||
{
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
strcpy(*outs, ucstombs_out);
|
||||
free(ucstombs_out);
|
||||
ucstombs_out = NULL;
|
||||
}
|
||||
|
||||
if (len == -1 && errno == EILSEQ)
|
||||
{
|
||||
// The string could not be converted to the current local,
|
||||
// do it manually by replacing non-ASCII characters with underscores
|
||||
if (!*outs || outs_len >= ins_len)
|
||||
{
|
||||
if (!*outs)
|
||||
{
|
||||
*outs = (char *) ntfs_alloc(ins_len + 1);
|
||||
if (!*outs) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < ins_len; i++) {
|
||||
ntfschar uc = le16_to_cpu(ins[i]);
|
||||
if (uc > 0xff)
|
||||
uc = (ntfschar)'_';
|
||||
*outs[i] = (char)uc;
|
||||
}
|
||||
*outs[ins_len] = (ntfschar)'\0';
|
||||
len = ins_len;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int ntfsLocalToUnicode (const char *ins, ntfschar **outs)
|
||||
{
|
||||
// Sanity check
|
||||
if (!ins || !outs)
|
||||
return 0;
|
||||
|
||||
// Convert the local string to unicode
|
||||
return ntfs_mbstoucs(ins, outs);
|
||||
}
|
178
source/ntfsinternal.h
Normal file
178
source/ntfsinternal.h
Normal file
@ -0,0 +1,178 @@
|
||||
/**
|
||||
* ntfsinternal.h - Internal support routines for NTFS-based devices.
|
||||
*
|
||||
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
||||
* Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFSINTERNAL_H
|
||||
#define _NTFSINTERNAL_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "compat.h"
|
||||
#include "logging.h"
|
||||
#include "layout.h"
|
||||
#include "device.h"
|
||||
#include "volume.h"
|
||||
#include "dir.h"
|
||||
#include "inode.h"
|
||||
#include "attrib.h"
|
||||
#include "reparse.h"
|
||||
#include "security.h"
|
||||
#include "efs.h"
|
||||
#include "unistr.h"
|
||||
|
||||
#include <gccore.h>
|
||||
#include <ogc/disc_io.h>
|
||||
#include <sys/iosupport.h>
|
||||
|
||||
#define NTFS_MOUNT_PREFIX "ntfs" /* Device name prefix to use when auto-mounting */
|
||||
#define NTFS_MAX_PARTITIONS 32 /* Maximum number of partitions that can be found */
|
||||
#define NTFS_MAX_MOUNTS 10 /* Maximum number of mounts available at one time */
|
||||
#define NTFS_MAX_SYMLINK_DEPTH 10 /* Maximum search depth when resolving symbolic links */
|
||||
|
||||
#define NTFS_OEM_ID cpu_to_le64(0x202020205346544eULL) /* "NTFS " */
|
||||
|
||||
#define MBR_SIGNATURE cpu_to_le16(0xAA55)
|
||||
#define EBR_SIGNATURE cpu_to_le16(0xAA55)
|
||||
|
||||
#define PARTITION_STATUS_NONBOOTABLE 0x00 /* Non-bootable */
|
||||
#define PARTITION_STATUS_BOOTABLE 0x80 /* Bootable (active) */
|
||||
|
||||
#define PARTITION_TYPE_EMPTY 0x00 /* Empty */
|
||||
#define PARTITION_TYPE_DOS33_EXTENDED 0x05 /* DOS 3.3+ extended partition */
|
||||
#define PARTITION_TYPE_NTFS 0x07 /* Windows NT NTFS */
|
||||
#define PARTITION_TYPE_WIN95_EXTENDED 0x0F /* Windows 95 extended partition */
|
||||
|
||||
/* Forward declarations */
|
||||
struct _ntfs_file_state;
|
||||
struct _ntfs_dir_state;
|
||||
|
||||
/**
|
||||
* PRIMARY_PARTITION - Block device partition record
|
||||
*/
|
||||
typedef struct _PARTITION_RECORD {
|
||||
u8 status; /* Partition status; see above */
|
||||
u8 chs_start[3]; /* Cylinder-head-sector address to first block of partition */
|
||||
u8 type; /* Partition type; see above */
|
||||
u8 chs_end[3]; /* Cylinder-head-sector address to last block of partition */
|
||||
u32 lba_start; /* Local block address to first sector of partition */
|
||||
u32 block_count; /* Number of blocks in partition */
|
||||
} __attribute__((__packed__)) PARTITION_RECORD;
|
||||
|
||||
/**
|
||||
* MASTER_BOOT_RECORD - Block device master boot record
|
||||
*/
|
||||
typedef struct _MASTER_BOOT_RECORD {
|
||||
u8 code_area[446]; /* Code area; normally empty */
|
||||
PARTITION_RECORD partitions[4]; /* 4 primary partitions */
|
||||
u16 signature; /* MBR signature; 0xAA55 */
|
||||
} __attribute__((__packed__)) MASTER_BOOT_RECORD;
|
||||
|
||||
/**
|
||||
* EXTENDED_PARTITION - Block device extended boot record
|
||||
*/
|
||||
typedef struct _EXTENDED_BOOT_RECORD {
|
||||
u8 code_area[446]; /* Code area; normally empty */
|
||||
PARTITION_RECORD partition; /* Primary partition */
|
||||
PARTITION_RECORD next_ebr; /* Next extended boot record in the chain */
|
||||
u8 reserved[32]; /* Normally empty */
|
||||
u16 signature; /* EBR signature; 0xAA55 */
|
||||
} __attribute__((__packed__)) EXTENDED_BOOT_RECORD;
|
||||
|
||||
/**
|
||||
* INTERFACE_ID - Disc interface identifier
|
||||
*/
|
||||
typedef struct _INTERFACE_ID {
|
||||
const char *name; /* Interface name */
|
||||
const DISC_INTERFACE *interface; /* Disc interface */
|
||||
} INTERFACE_ID;
|
||||
|
||||
/**
|
||||
* ntfs_atime_t - File access time update strategies
|
||||
*/
|
||||
typedef enum {
|
||||
ATIME_ENABLED, /* Update access times */
|
||||
ATIME_DISABLED /* Don't update access times */
|
||||
} ntfs_atime_t;
|
||||
|
||||
/**
|
||||
* ntfs_vd - NTFS volume descriptor
|
||||
*/
|
||||
typedef struct _ntfs_vd {
|
||||
struct ntfs_device *dev; /* NTFS device handle */
|
||||
ntfs_volume *vol; /* NTFS volume handle */
|
||||
mutex_t lock; /* Volume lock mutex */
|
||||
s64 id; /* Filesystem id */
|
||||
u32 flags; /* Mount flags */
|
||||
char name[128]; /* Volume name (cached) */
|
||||
u16 uid; /* User id for entry creation */
|
||||
u16 gid; /* Group id for entry creation */
|
||||
u16 fmask; /* Unix style permission mask for file creation */
|
||||
u16 dmask; /* Unix style permission mask for directory creation */
|
||||
ntfs_atime_t atime; /* Entry access time update strategy */
|
||||
bool showHiddenFiles; /* If true, show hidden files when enumerating directories */
|
||||
bool showSystemFiles; /* If true, show system files when enumerating directories */
|
||||
ntfs_inode *cwd_ni; /* Current directory */
|
||||
struct _ntfs_dir_state *firstOpenDir; /* The start of a FILO linked list of currently opened directories */
|
||||
struct _ntfs_file_state *firstOpenFile; /* The start of a FILO linked list of currently opened files */
|
||||
u16 openDirCount; /* The total number of directories currently open in this volume */
|
||||
u16 openFileCount; /* The total number of files currently open in this volume */
|
||||
} ntfs_vd;
|
||||
|
||||
/* Lock volume */
|
||||
static inline void ntfsLock (ntfs_vd *vd)
|
||||
{
|
||||
LWP_MutexLock(vd->lock);
|
||||
}
|
||||
|
||||
/* Unlock volume */
|
||||
static inline void ntfsUnlock (ntfs_vd *vd)
|
||||
{
|
||||
LWP_MutexUnlock(vd->lock);
|
||||
}
|
||||
|
||||
/* Gekko device related routines */
|
||||
int ntfsAddDevice (const char *name, void *deviceData);
|
||||
void ntfsRemoveDevice (const char *path);
|
||||
const devoptab_t *ntfsGetDevice (const char *path, bool useDefaultDevice);
|
||||
const devoptab_t *ntfsGetDevOpTab (void);
|
||||
const INTERFACE_ID* ntfsGetDiscInterfaces (void);
|
||||
|
||||
/* Miscellaneous helper/support routines */
|
||||
int ntfsInitVolume (ntfs_vd *vd);
|
||||
void ntfsDeinitVolume (ntfs_vd *vd);
|
||||
ntfs_vd *ntfsGetVolume (const char *path);
|
||||
ntfs_inode *ntfsOpenEntry (ntfs_vd *vd, const char *path);
|
||||
ntfs_inode *ntfsParseEntry (ntfs_vd *vd, const char *path, int reparseLevel);
|
||||
void ntfsCloseEntry (ntfs_vd *vd, ntfs_inode *ni);
|
||||
ntfs_inode *ntfsCreate (ntfs_vd *vd, const char *path, mode_t type, const char *target);
|
||||
int ntfsLink (ntfs_vd *vd, const char *old_path, const char *new_path);
|
||||
int ntfsUnlink (ntfs_vd *vd, const char *path);
|
||||
int ntfsSync (ntfs_vd *vd, ntfs_inode *ni);
|
||||
int ntfsStat (ntfs_vd *vd, ntfs_inode *ni, struct stat *st);
|
||||
void ntfsUpdateTimes (ntfs_vd *vd, ntfs_inode *ni, ntfs_time_update_flags mask);
|
||||
|
||||
const char *ntfsRealPath (const char *path);
|
||||
int ntfsUnicodeToLocal (const ntfschar *ins, const int ins_len, char **outs, int outs_len);
|
||||
int ntfsLocalToUnicode (const char *ins, ntfschar **outs);
|
||||
|
||||
#endif /* _NTFSINTERNAL_H */
|
121
source/ntfstime.h
Normal file
121
source/ntfstime.h
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* ntfstime.h - NTFS time related functions. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2005 Anton Altaparmakov
|
||||
* Copyright (c) 2005 Yura Pakhuchiy
|
||||
* Copyright (c) 2010 Jean-Pierre Andre
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_NTFSTIME_H
|
||||
#define _NTFS_NTFSTIME_H
|
||||
|
||||
#ifdef HAVE_TIME_H
|
||||
#include <time.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_GETTIMEOFDAY
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/*
|
||||
* There are four times more conversions of internal representation
|
||||
* to ntfs representation than any other conversion, so the most
|
||||
* efficient internal representation is ntfs representation
|
||||
* (with low endianness)
|
||||
*/
|
||||
typedef sle64 ntfs_time;
|
||||
|
||||
#define NTFS_TIME_OFFSET ((s64)(369 * 365 + 89) * 24 * 3600 * 10000000)
|
||||
|
||||
/**
|
||||
* ntfs2timespec - Convert an NTFS time to Unix time
|
||||
* @ntfs_time: An NTFS time in 100ns units since 1601
|
||||
*
|
||||
* NTFS stores times as the number of 100ns intervals since January 1st 1601 at
|
||||
* 00:00 UTC. This system will not suffer from Y2K problems until ~57000AD.
|
||||
*
|
||||
* Return: A Unix time (number of seconds since 1970, and nanoseconds)
|
||||
*/
|
||||
static __inline__ struct timespec ntfs2timespec(ntfs_time ntfstime)
|
||||
{
|
||||
struct timespec spec;
|
||||
s64 cputime;
|
||||
|
||||
cputime = sle64_to_cpu(ntfstime);
|
||||
spec.tv_sec = (cputime - (NTFS_TIME_OFFSET)) / 10000000;
|
||||
spec.tv_nsec = (cputime - (NTFS_TIME_OFFSET)
|
||||
- (s64)spec.tv_sec*10000000)*100;
|
||||
/* force zero nsec for overflowing dates */
|
||||
if ((spec.tv_nsec < 0) || (spec.tv_nsec > 999999999))
|
||||
spec.tv_nsec = 0;
|
||||
return (spec);
|
||||
}
|
||||
|
||||
/**
|
||||
* timespec2ntfs - Convert Linux time to NTFS time
|
||||
* @utc_time: Linux time to convert to NTFS
|
||||
*
|
||||
* Convert the Linux time @utc_time to its corresponding NTFS time.
|
||||
*
|
||||
* Linux stores time in a long at present and measures it as the number of
|
||||
* 1-second intervals since 1st January 1970, 00:00:00 UTC
|
||||
* with a separated non-negative nanosecond value
|
||||
*
|
||||
* NTFS uses Microsoft's standard time format which is stored in a sle64 and is
|
||||
* measured as the number of 100 nano-second intervals since 1st January 1601,
|
||||
* 00:00:00 UTC.
|
||||
*
|
||||
* Return: An NTFS time (100ns units since Jan 1601)
|
||||
*/
|
||||
static __inline__ ntfs_time timespec2ntfs(struct timespec spec)
|
||||
{
|
||||
s64 units;
|
||||
|
||||
units = (s64)spec.tv_sec * 10000000
|
||||
+ NTFS_TIME_OFFSET + spec.tv_nsec/100;
|
||||
return (cpu_to_le64(units));
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the current time in ntfs format
|
||||
*/
|
||||
|
||||
static __inline__ ntfs_time ntfs_current_time(void)
|
||||
{
|
||||
struct timespec now;
|
||||
|
||||
#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_SYS_CLOCK_GETTIME)
|
||||
clock_gettime(CLOCK_REALTIME, &now);
|
||||
#elif defined(HAVE_GETTIMEOFDAY)
|
||||
struct timeval microseconds;
|
||||
|
||||
gettimeofday(µseconds, (struct timezone*)NULL);
|
||||
now.tv_sec = microseconds.tv_sec;
|
||||
now.tv_nsec = microseconds.tv_usec*1000;
|
||||
#else
|
||||
now.tv_sec = time((time_t*)NULL);
|
||||
now.tv_nsec = 0;
|
||||
#endif
|
||||
return (timespec2ntfs(now));
|
||||
}
|
||||
|
||||
#endif /* _NTFS_NTFSTIME_H */
|
637
source/object_id.c
Normal file
637
source/object_id.c
Normal file
@ -0,0 +1,637 @@
|
||||
/**
|
||||
* object_id.c - Processing of object ids
|
||||
*
|
||||
* This module is part of ntfs-3g library
|
||||
*
|
||||
* Copyright (c) 2009 Jean-Pierre Andre
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SETXATTR
|
||||
#include <sys/xattr.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_SYSMACROS_H
|
||||
#include <sys/sysmacros.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "debug.h"
|
||||
#include "attrib.h"
|
||||
#include "inode.h"
|
||||
#include "dir.h"
|
||||
#include "volume.h"
|
||||
#include "mft.h"
|
||||
#include "index.h"
|
||||
#include "lcnalloc.h"
|
||||
#include "object_id.h"
|
||||
#include "logging.h"
|
||||
#include "misc.h"
|
||||
|
||||
/*
|
||||
* Endianness considerations
|
||||
*
|
||||
* According to RFC 4122, GUIDs should be printed with the most
|
||||
* significant byte first, and the six fields be compared individually
|
||||
* for ordering. RFC 4122 does not define the internal representation.
|
||||
*
|
||||
* Here we always copy disk images with no endianness change,
|
||||
* and, for indexing, GUIDs are compared as if they were a sequence
|
||||
* of four unsigned 32 bit integers.
|
||||
*
|
||||
* --------------------- begin from RFC 4122 ----------------------
|
||||
* Consider each field of the UUID to be an unsigned integer as shown
|
||||
* in the table in section Section 4.1.2. Then, to compare a pair of
|
||||
* UUIDs, arithmetically compare the corresponding fields from each
|
||||
* UUID in order of significance and according to their data type.
|
||||
* Two UUIDs are equal if and only if all the corresponding fields
|
||||
* are equal.
|
||||
*
|
||||
* UUIDs, as defined in this document, can also be ordered
|
||||
* lexicographically. For a pair of UUIDs, the first one follows the
|
||||
* second if the most significant field in which the UUIDs differ is
|
||||
* greater for the first UUID. The second precedes the first if the
|
||||
* most significant field in which the UUIDs differ is greater for
|
||||
* the second UUID.
|
||||
*
|
||||
* The fields are encoded as 16 octets, with the sizes and order of the
|
||||
* fields defined above, and with each field encoded with the Most
|
||||
* Significant Byte first (known as network byte order). Note that the
|
||||
* field names, particularly for multiplexed fields, follow historical
|
||||
* practice.
|
||||
*
|
||||
* 0 1 2 3
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | time_low |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | time_mid | time_hi_and_version |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* |clk_seq_hi_res | clk_seq_low | node (0-1) |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | node (2-5) |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*
|
||||
* ---------------------- end from RFC 4122 -----------------------
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
GUID object_id;
|
||||
} OBJECT_ID_INDEX_KEY;
|
||||
|
||||
typedef struct {
|
||||
le64 file_id;
|
||||
GUID birth_volume_id;
|
||||
GUID birth_object_id;
|
||||
GUID domain_id;
|
||||
} OBJECT_ID_INDEX_DATA; // known as OBJ_ID_INDEX_DATA
|
||||
|
||||
struct OBJECT_ID_INDEX { /* index entry in $Extend/$ObjId */
|
||||
INDEX_ENTRY_HEADER header;
|
||||
OBJECT_ID_INDEX_KEY key;
|
||||
OBJECT_ID_INDEX_DATA data;
|
||||
} ;
|
||||
|
||||
static ntfschar objid_index_name[] = { const_cpu_to_le16('$'),
|
||||
const_cpu_to_le16('O') };
|
||||
#ifdef HAVE_SETXATTR /* extended attributes interface required */
|
||||
|
||||
/*
|
||||
* Set the index for a new object id
|
||||
*
|
||||
* Returns 0 if success
|
||||
* -1 if failure, explained by errno
|
||||
*/
|
||||
|
||||
static int set_object_id_index(ntfs_inode *ni, ntfs_index_context *xo,
|
||||
const OBJECT_ID_ATTR *object_id)
|
||||
{
|
||||
struct OBJECT_ID_INDEX indx;
|
||||
u64 file_id_cpu;
|
||||
le64 file_id;
|
||||
le16 seqn;
|
||||
|
||||
seqn = ni->mrec->sequence_number;
|
||||
file_id_cpu = MK_MREF(ni->mft_no,le16_to_cpu(seqn));
|
||||
file_id = cpu_to_le64(file_id_cpu);
|
||||
indx.header.data_offset = const_cpu_to_le16(
|
||||
sizeof(INDEX_ENTRY_HEADER)
|
||||
+ sizeof(OBJECT_ID_INDEX_KEY));
|
||||
indx.header.data_length = const_cpu_to_le16(
|
||||
sizeof(OBJECT_ID_INDEX_DATA));
|
||||
indx.header.reservedV = const_cpu_to_le32(0);
|
||||
indx.header.length = const_cpu_to_le16(
|
||||
sizeof(struct OBJECT_ID_INDEX));
|
||||
indx.header.key_length = const_cpu_to_le16(
|
||||
sizeof(OBJECT_ID_INDEX_KEY));
|
||||
indx.header.flags = const_cpu_to_le16(0);
|
||||
indx.header.reserved = const_cpu_to_le16(0);
|
||||
|
||||
memcpy(&indx.key.object_id,object_id,sizeof(GUID));
|
||||
|
||||
indx.data.file_id = file_id;
|
||||
memcpy(&indx.data.birth_volume_id,
|
||||
&object_id->birth_volume_id,sizeof(GUID));
|
||||
memcpy(&indx.data.birth_object_id,
|
||||
&object_id->birth_object_id,sizeof(GUID));
|
||||
memcpy(&indx.data.domain_id,
|
||||
&object_id->domain_id,sizeof(GUID));
|
||||
ntfs_index_ctx_reinit(xo);
|
||||
return (ntfs_ie_add(xo,(INDEX_ENTRY*)&indx));
|
||||
}
|
||||
|
||||
#endif /* HAVE_SETXATTR */
|
||||
|
||||
/*
|
||||
* Open the $Extend/$ObjId file and its index
|
||||
*
|
||||
* Return the index context if opened
|
||||
* or NULL if an error occurred (errno tells why)
|
||||
*
|
||||
* The index has to be freed and inode closed when not needed any more.
|
||||
*/
|
||||
|
||||
static ntfs_index_context *open_object_id_index(ntfs_volume *vol)
|
||||
{
|
||||
u64 inum;
|
||||
ntfs_inode *ni;
|
||||
ntfs_inode *dir_ni;
|
||||
ntfs_index_context *xo;
|
||||
|
||||
/* do not use path_name_to inode - could reopen root */
|
||||
dir_ni = ntfs_inode_open(vol, FILE_Extend);
|
||||
ni = (ntfs_inode*)NULL;
|
||||
if (dir_ni) {
|
||||
inum = ntfs_inode_lookup_by_mbsname(dir_ni,"$ObjId");
|
||||
if (inum != (u64)-1)
|
||||
ni = ntfs_inode_open(vol, inum);
|
||||
ntfs_inode_close(dir_ni);
|
||||
}
|
||||
if (ni) {
|
||||
xo = ntfs_index_ctx_get(ni, objid_index_name, 2);
|
||||
if (!xo) {
|
||||
ntfs_inode_close(ni);
|
||||
}
|
||||
} else
|
||||
xo = (ntfs_index_context*)NULL;
|
||||
return (xo);
|
||||
}
|
||||
|
||||
#ifdef HAVE_SETXATTR /* extended attributes interface required */
|
||||
|
||||
/*
|
||||
* Merge object_id data stored in the index into
|
||||
* a full object_id struct.
|
||||
*
|
||||
* returns 0 if merging successful
|
||||
* -1 if no data could be merged. This is generally not an error
|
||||
*/
|
||||
|
||||
static int merge_index_data(ntfs_inode *ni,
|
||||
const OBJECT_ID_ATTR *objectid_attr,
|
||||
OBJECT_ID_ATTR *full_objectid)
|
||||
{
|
||||
OBJECT_ID_INDEX_KEY key;
|
||||
struct OBJECT_ID_INDEX *entry;
|
||||
ntfs_index_context *xo;
|
||||
ntfs_inode *xoni;
|
||||
int res;
|
||||
|
||||
res = -1;
|
||||
xo = open_object_id_index(ni->vol);
|
||||
if (xo) {
|
||||
memcpy(&key.object_id,objectid_attr,sizeof(GUID));
|
||||
if (!ntfs_index_lookup(&key,
|
||||
sizeof(OBJECT_ID_INDEX_KEY), xo)) {
|
||||
entry = (struct OBJECT_ID_INDEX*)xo->entry;
|
||||
/* make sure inode numbers match */
|
||||
if (entry
|
||||
&& (MREF(le64_to_cpu(entry->data.file_id))
|
||||
== ni->mft_no)) {
|
||||
memcpy(&full_objectid->birth_volume_id,
|
||||
&entry->data.birth_volume_id,
|
||||
sizeof(GUID));
|
||||
memcpy(&full_objectid->birth_object_id,
|
||||
&entry->data.birth_object_id,
|
||||
sizeof(GUID));
|
||||
memcpy(&full_objectid->domain_id,
|
||||
&entry->data.domain_id,
|
||||
sizeof(GUID));
|
||||
res = 0;
|
||||
}
|
||||
}
|
||||
xoni = xo->ni;
|
||||
ntfs_index_ctx_put(xo);
|
||||
ntfs_inode_close(xoni);
|
||||
}
|
||||
return (res);
|
||||
}
|
||||
|
||||
#endif /* HAVE_SETXATTR */
|
||||
|
||||
/*
|
||||
* Remove an object id index entry if attribute present
|
||||
*
|
||||
* Returns the size of existing object id
|
||||
* (the existing object_d is returned)
|
||||
* -1 if failure, explained by errno
|
||||
*/
|
||||
|
||||
static int remove_object_id_index(ntfs_attr *na, ntfs_index_context *xo,
|
||||
OBJECT_ID_ATTR *old_attr)
|
||||
{
|
||||
OBJECT_ID_INDEX_KEY key;
|
||||
struct OBJECT_ID_INDEX *entry;
|
||||
s64 size;
|
||||
int ret;
|
||||
|
||||
ret = na->data_size;
|
||||
if (ret) {
|
||||
/* read the existing object id attribute */
|
||||
size = ntfs_attr_pread(na, 0, sizeof(GUID), old_attr);
|
||||
if (size >= (s64)sizeof(GUID)) {
|
||||
memcpy(&key.object_id,
|
||||
&old_attr->object_id,sizeof(GUID));
|
||||
size = sizeof(GUID);
|
||||
if (!ntfs_index_lookup(&key,
|
||||
sizeof(OBJECT_ID_INDEX_KEY), xo)) {
|
||||
entry = (struct OBJECT_ID_INDEX*)xo->entry;
|
||||
memcpy(&old_attr->birth_volume_id,
|
||||
&entry->data.birth_volume_id,
|
||||
sizeof(GUID));
|
||||
memcpy(&old_attr->birth_object_id,
|
||||
&entry->data.birth_object_id,
|
||||
sizeof(GUID));
|
||||
memcpy(&old_attr->domain_id,
|
||||
&entry->data.domain_id,
|
||||
sizeof(GUID));
|
||||
size = sizeof(OBJECT_ID_ATTR);
|
||||
if (ntfs_index_rm(xo))
|
||||
ret = -1;
|
||||
}
|
||||
} else {
|
||||
ret = -1;
|
||||
errno = ENODATA;
|
||||
}
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
#ifdef HAVE_SETXATTR /* extended attributes interface required */
|
||||
|
||||
/*
|
||||
* Update the object id and index
|
||||
*
|
||||
* The object_id attribute should have been created and the
|
||||
* non-duplication of the GUID should have been checked before.
|
||||
*
|
||||
* Returns 0 if success
|
||||
* -1 if failure, explained by errno
|
||||
* If could not remove the existing index, nothing is done,
|
||||
* If could not write the new data, no index entry is inserted
|
||||
* If failed to insert the index, data is removed
|
||||
*/
|
||||
|
||||
static int update_object_id(ntfs_inode *ni, ntfs_index_context *xo,
|
||||
const OBJECT_ID_ATTR *value, size_t size)
|
||||
{
|
||||
OBJECT_ID_ATTR old_attr;
|
||||
ntfs_attr *na;
|
||||
int oldsize;
|
||||
int written;
|
||||
int res;
|
||||
|
||||
res = 0;
|
||||
|
||||
na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0);
|
||||
if (na) {
|
||||
|
||||
/* remove the existing index entry */
|
||||
oldsize = remove_object_id_index(na,xo,&old_attr);
|
||||
if (oldsize < 0)
|
||||
res = -1;
|
||||
else {
|
||||
/* resize attribute */
|
||||
res = ntfs_attr_truncate(na, (s64)sizeof(GUID));
|
||||
/* write the object_id in attribute */
|
||||
if (!res && value) {
|
||||
written = (int)ntfs_attr_pwrite(na,
|
||||
(s64)0, (s64)sizeof(GUID),
|
||||
&value->object_id);
|
||||
if (written != (s64)sizeof(GUID)) {
|
||||
ntfs_log_error("Failed to update "
|
||||
"object id\n");
|
||||
errno = EIO;
|
||||
res = -1;
|
||||
}
|
||||
}
|
||||
/* write index part if provided */
|
||||
if (!res
|
||||
&& ((size < sizeof(OBJECT_ID_ATTR))
|
||||
|| set_object_id_index(ni,xo,value))) {
|
||||
/*
|
||||
* If cannot index, try to remove the object
|
||||
* id and log the error. There will be an
|
||||
* inconsistency if removal fails.
|
||||
*/
|
||||
ntfs_attr_rm(na);
|
||||
ntfs_log_error("Failed to index object id."
|
||||
" Possible corruption.\n");
|
||||
}
|
||||
}
|
||||
ntfs_attr_close(na);
|
||||
NInoSetDirty(ni);
|
||||
} else
|
||||
res = -1;
|
||||
return (res);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a (dummy) object id to an inode if it does not exist
|
||||
*
|
||||
* returns 0 if attribute was inserted (or already present)
|
||||
* -1 if adding failed (explained by errno)
|
||||
*/
|
||||
|
||||
static int add_object_id(ntfs_inode *ni, int flags)
|
||||
{
|
||||
int res;
|
||||
u8 dummy;
|
||||
|
||||
res = -1; /* default return */
|
||||
if (!ntfs_attr_exist(ni,AT_OBJECT_ID, AT_UNNAMED,0)) {
|
||||
if (!(flags & XATTR_REPLACE)) {
|
||||
/*
|
||||
* no object id attribute : add one,
|
||||
* apparently, this does not feed the new value in
|
||||
* Note : NTFS version must be >= 3
|
||||
*/
|
||||
if (ni->vol->major_ver >= 3) {
|
||||
res = ntfs_attr_add(ni, AT_OBJECT_ID,
|
||||
AT_UNNAMED, 0, &dummy, (s64)0);
|
||||
NInoSetDirty(ni);
|
||||
} else
|
||||
errno = EOPNOTSUPP;
|
||||
} else
|
||||
errno = ENODATA;
|
||||
} else {
|
||||
if (flags & XATTR_CREATE)
|
||||
errno = EEXIST;
|
||||
else
|
||||
res = 0;
|
||||
}
|
||||
return (res);
|
||||
}
|
||||
|
||||
#endif /* HAVE_SETXATTR */
|
||||
|
||||
/*
|
||||
* Delete an object_id index entry
|
||||
*
|
||||
* Returns 0 if success
|
||||
* -1 if failure, explained by errno
|
||||
*/
|
||||
|
||||
int ntfs_delete_object_id_index(ntfs_inode *ni)
|
||||
{
|
||||
ntfs_index_context *xo;
|
||||
ntfs_inode *xoni;
|
||||
ntfs_attr *na;
|
||||
OBJECT_ID_ATTR old_attr;
|
||||
int res;
|
||||
|
||||
res = 0;
|
||||
na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0);
|
||||
if (na) {
|
||||
/*
|
||||
* read the existing object id
|
||||
* and un-index it
|
||||
*/
|
||||
xo = open_object_id_index(ni->vol);
|
||||
if (xo) {
|
||||
if (remove_object_id_index(na,xo,&old_attr) < 0)
|
||||
res = -1;
|
||||
xoni = xo->ni;
|
||||
ntfs_index_entry_mark_dirty(xo);
|
||||
NInoSetDirty(xoni);
|
||||
ntfs_index_ctx_put(xo);
|
||||
ntfs_inode_close(xoni);
|
||||
}
|
||||
ntfs_attr_close(na);
|
||||
}
|
||||
return (res);
|
||||
}
|
||||
|
||||
#ifdef HAVE_SETXATTR /* extended attributes interface required */
|
||||
|
||||
/*
|
||||
* Get the ntfs object id into an extended attribute
|
||||
*
|
||||
* If present, the object_id from the attribute and the GUIDs
|
||||
* from the index are returned (formatted as OBJECT_ID_ATTR)
|
||||
*
|
||||
* Returns the global size (can be 0, 16 or 64)
|
||||
* and the buffer is updated if it is long enough
|
||||
*/
|
||||
|
||||
int ntfs_get_ntfs_object_id(ntfs_inode *ni, char *value, size_t size)
|
||||
{
|
||||
OBJECT_ID_ATTR full_objectid;
|
||||
OBJECT_ID_ATTR *objectid_attr;
|
||||
s64 attr_size;
|
||||
int full_size;
|
||||
|
||||
full_size = 0; /* default to no data and some error to be defined */
|
||||
if (ni) {
|
||||
objectid_attr = (OBJECT_ID_ATTR*)ntfs_attr_readall(ni,
|
||||
AT_OBJECT_ID,(ntfschar*)NULL, 0, &attr_size);
|
||||
if (objectid_attr) {
|
||||
/* restrict to only GUID present in attr */
|
||||
if (attr_size == sizeof(GUID)) {
|
||||
memcpy(&full_objectid.object_id,
|
||||
objectid_attr,sizeof(GUID));
|
||||
full_size = sizeof(GUID);
|
||||
/* get data from index, if any */
|
||||
if (!merge_index_data(ni, objectid_attr,
|
||||
&full_objectid)) {
|
||||
full_size = sizeof(OBJECT_ID_ATTR);
|
||||
}
|
||||
if (full_size <= (s64)size) {
|
||||
if (value)
|
||||
memcpy(value,&full_objectid,
|
||||
full_size);
|
||||
else
|
||||
errno = EINVAL;
|
||||
}
|
||||
} else {
|
||||
/* unexpected size, better return unsupported */
|
||||
errno = EOPNOTSUPP;
|
||||
full_size = 0;
|
||||
}
|
||||
free(objectid_attr);
|
||||
} else
|
||||
errno = ENODATA;
|
||||
}
|
||||
return (full_size ? (int)full_size : -errno);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the object id from an extended attribute
|
||||
*
|
||||
* If the size is 64, the attribute and index are set.
|
||||
* else if the size is not less than 16 only the attribute is set.
|
||||
* The object id index is set accordingly.
|
||||
*
|
||||
* Returns 0, or -1 if there is a problem
|
||||
*/
|
||||
|
||||
int ntfs_set_ntfs_object_id(ntfs_inode *ni,
|
||||
const char *value, size_t size, int flags)
|
||||
{
|
||||
OBJECT_ID_INDEX_KEY key;
|
||||
ntfs_inode *xoni;
|
||||
ntfs_index_context *xo;
|
||||
int res;
|
||||
|
||||
res = 0;
|
||||
if (ni && value && (size >= sizeof(GUID))) {
|
||||
xo = open_object_id_index(ni->vol);
|
||||
if (xo) {
|
||||
/* make sure the GUID was not used somewhere */
|
||||
memcpy(&key.object_id, value, sizeof(GUID));
|
||||
if (ntfs_index_lookup(&key,
|
||||
sizeof(OBJECT_ID_INDEX_KEY), xo)) {
|
||||
ntfs_index_ctx_reinit(xo);
|
||||
res = add_object_id(ni, flags);
|
||||
if (!res) {
|
||||
/* update value and index */
|
||||
res = update_object_id(ni,xo,
|
||||
(const OBJECT_ID_ATTR*)value,
|
||||
size);
|
||||
}
|
||||
} else {
|
||||
/* GUID is present elsewhere */
|
||||
res = -1;
|
||||
errno = EEXIST;
|
||||
}
|
||||
xoni = xo->ni;
|
||||
ntfs_index_entry_mark_dirty(xo);
|
||||
NInoSetDirty(xoni);
|
||||
ntfs_index_ctx_put(xo);
|
||||
ntfs_inode_close(xoni);
|
||||
} else {
|
||||
res = -1;
|
||||
}
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
res = -1;
|
||||
}
|
||||
return (res ? -1 : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove the object id
|
||||
*
|
||||
* Returns 0, or -1 if there is a problem
|
||||
*/
|
||||
|
||||
int ntfs_remove_ntfs_object_id(ntfs_inode *ni)
|
||||
{
|
||||
int res;
|
||||
int olderrno;
|
||||
ntfs_attr *na;
|
||||
ntfs_inode *xoni;
|
||||
ntfs_index_context *xo;
|
||||
int oldsize;
|
||||
OBJECT_ID_ATTR old_attr;
|
||||
|
||||
res = 0;
|
||||
if (ni) {
|
||||
/*
|
||||
* open and delete the object id
|
||||
*/
|
||||
na = ntfs_attr_open(ni, AT_OBJECT_ID,
|
||||
AT_UNNAMED,0);
|
||||
if (na) {
|
||||
/* first remove index (old object id needed) */
|
||||
xo = open_object_id_index(ni->vol);
|
||||
if (xo) {
|
||||
oldsize = remove_object_id_index(na,xo,
|
||||
&old_attr);
|
||||
if (oldsize < 0) {
|
||||
res = -1;
|
||||
} else {
|
||||
/* now remove attribute */
|
||||
res = ntfs_attr_rm(na);
|
||||
if (res
|
||||
&& (oldsize > (int)sizeof(GUID))) {
|
||||
/*
|
||||
* If we could not remove the
|
||||
* attribute, try to restore the
|
||||
* index and log the error. There
|
||||
* will be an inconsistency if
|
||||
* the reindexing fails.
|
||||
*/
|
||||
set_object_id_index(ni, xo,
|
||||
&old_attr);
|
||||
ntfs_log_error(
|
||||
"Failed to remove object id."
|
||||
" Possible corruption.\n");
|
||||
}
|
||||
}
|
||||
|
||||
xoni = xo->ni;
|
||||
ntfs_index_entry_mark_dirty(xo);
|
||||
NInoSetDirty(xoni);
|
||||
ntfs_index_ctx_put(xo);
|
||||
ntfs_inode_close(xoni);
|
||||
}
|
||||
olderrno = errno;
|
||||
ntfs_attr_close(na);
|
||||
/* avoid errno pollution */
|
||||
if (errno == ENOENT)
|
||||
errno = olderrno;
|
||||
} else {
|
||||
errno = ENODATA;
|
||||
res = -1;
|
||||
}
|
||||
NInoSetDirty(ni);
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
res = -1;
|
||||
}
|
||||
return (res ? -1 : 0);
|
||||
}
|
||||
|
||||
#endif /* HAVE_SETXATTR */
|
35
source/object_id.h
Normal file
35
source/object_id.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2008 Jean-Pierre Andre
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef OBJECT_ID_H
|
||||
#define OBJECT_ID_H
|
||||
|
||||
int ntfs_get_ntfs_object_id(ntfs_inode *ni, char *value, size_t size);
|
||||
|
||||
int ntfs_set_ntfs_object_id(ntfs_inode *ni, const char *value,
|
||||
size_t size, int flags);
|
||||
int ntfs_remove_ntfs_object_id(ntfs_inode *ni);
|
||||
|
||||
int ntfs_delete_object_id_index(ntfs_inode *ni);
|
||||
|
||||
#endif /* OBJECT_ID_H */
|
82
source/param.h
Normal file
82
source/param.h
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* param.h - Parameter values for ntfs-3g
|
||||
*
|
||||
* Copyright (c) 2009 Jean-Pierre Andre
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_PARAM_H
|
||||
#define _NTFS_PARAM_H
|
||||
|
||||
#define CACHE_INODE_SIZE 32 /* inode cache, zero or >= 3 and not too big */
|
||||
#define CACHE_NIDATA_SIZE 64 /* idata cache, zero or >= 3 and not too big */
|
||||
#define CACHE_LOOKUP_SIZE 64 /* lookup cache, zero or >= 3 and not too big */
|
||||
#define CACHE_SECURID_SIZE 16 /* securid cache, zero or >= 3 and not too big */
|
||||
#define CACHE_LEGACY_SIZE 8 /* legacy cache size, zero or >= 3 and not too big */
|
||||
|
||||
#define FORCE_FORMAT_v1x 0 /* Insert security data as in NTFS v1.x */
|
||||
#define OWNERFROMACL 1 /* Get the owner from ACL (not Windows owner) */
|
||||
|
||||
/* default security sub-authorities */
|
||||
enum {
|
||||
DEFSECAUTH1 = -1153374643, /* 3141592653 */
|
||||
DEFSECAUTH2 = 589793238,
|
||||
DEFSECAUTH3 = 462843383,
|
||||
DEFSECBASE = 10000
|
||||
};
|
||||
|
||||
/*
|
||||
* Parameters for compression
|
||||
*/
|
||||
|
||||
/* default option for compression */
|
||||
#define DEFAULT_COMPRESSION FALSE
|
||||
/* (log2 of) number of clusters in a compression block for new files */
|
||||
#define STANDARD_COMPRESSION_UNIT 4
|
||||
/* maximum cluster size for allowing compression for new files */
|
||||
#define MAX_COMPRESSION_CLUSTER_SIZE 4096
|
||||
|
||||
/*
|
||||
* Permission checking modes for high level and low level
|
||||
*
|
||||
* The choices for high and low lowel are independent, they have
|
||||
* no effect on the library
|
||||
*
|
||||
* Stick to the recommended values unless you understand the consequences
|
||||
* on protection and performances. Use of cacheing is good for
|
||||
* performances, but bad on security.
|
||||
*
|
||||
* Possible values for high level :
|
||||
* 1 : no cache, kernel control (recommended)
|
||||
* 4 : no cache, file system control
|
||||
* 7 : no cache, kernel control for ACLs
|
||||
*
|
||||
* Possible values for low level :
|
||||
* 2 : no cache, kernel control
|
||||
* 3 : use kernel/fuse cache, kernel control
|
||||
* 5 : no cache, file system control (recommended)
|
||||
* 8 : no cache, kernel control for ACLs
|
||||
*
|
||||
* Use of options 7 and 8 requires a patch to fuse
|
||||
* When Posix ACLs are selected in the configure options, a value
|
||||
* of 6 is added in the mount report.
|
||||
*/
|
||||
|
||||
#define HPERMSCONFIG 1
|
||||
#define LPERMSCONFIG 5
|
||||
|
||||
#endif /* defined _NTFS_PARAM_H */
|
1222
source/reparse.c
Normal file
1222
source/reparse.c
Normal file
File diff suppressed because it is too large
Load Diff
39
source/reparse.h
Normal file
39
source/reparse.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2008 Jean-Pierre Andre
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef REPARSE_H
|
||||
#define REPARSE_H
|
||||
|
||||
char *ntfs_make_symlink(ntfs_inode *ni, const char *mnt_point,
|
||||
int *pattr_size);
|
||||
BOOL ntfs_possible_symlink(ntfs_inode *ni);
|
||||
|
||||
int ntfs_get_ntfs_reparse_data(ntfs_inode *ni, char *value, size_t size);
|
||||
|
||||
int ntfs_set_ntfs_reparse_data(ntfs_inode *ni, const char *value,
|
||||
size_t size, int flags);
|
||||
int ntfs_remove_ntfs_reparse_data(ntfs_inode *ni);
|
||||
|
||||
int ntfs_delete_reparse_index(ntfs_inode *ni);
|
||||
|
||||
#endif /* REPARSE_H */
|
2181
source/runlist.c
Normal file
2181
source/runlist.c
Normal file
File diff suppressed because it is too large
Load Diff
90
source/runlist.h
Normal file
90
source/runlist.h
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* runlist.h - Exports for runlist handling. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2002 Anton Altaparmakov
|
||||
* Copyright (c) 2002 Richard Russon
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_RUNLIST_H
|
||||
#define _NTFS_RUNLIST_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/* Forward declarations */
|
||||
typedef struct _runlist_element runlist_element;
|
||||
typedef runlist_element runlist;
|
||||
|
||||
#include "attrib.h"
|
||||
#include "volume.h"
|
||||
|
||||
/**
|
||||
* struct _runlist_element - in memory vcn to lcn mapping array element.
|
||||
* @vcn: starting vcn of the current array element
|
||||
* @lcn: starting lcn of the current array element
|
||||
* @length: length in clusters of the current array element
|
||||
*
|
||||
* The last vcn (in fact the last vcn + 1) is reached when length == 0.
|
||||
*
|
||||
* When lcn == -1 this means that the count vcns starting at vcn are not
|
||||
* physically allocated (i.e. this is a hole / data is sparse).
|
||||
*/
|
||||
struct _runlist_element {/* In memory vcn to lcn mapping structure element. */
|
||||
VCN vcn; /* vcn = Starting virtual cluster number. */
|
||||
LCN lcn; /* lcn = Starting logical cluster number. */
|
||||
s64 length; /* Run length in clusters. */
|
||||
};
|
||||
|
||||
extern runlist_element *ntfs_rl_extend(ntfs_attr *na, runlist_element *rl,
|
||||
int more_entries);
|
||||
|
||||
extern LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn);
|
||||
|
||||
extern s64 ntfs_rl_pread(const ntfs_volume *vol, const runlist_element *rl,
|
||||
const s64 pos, s64 count, void *b);
|
||||
extern s64 ntfs_rl_pwrite(const ntfs_volume *vol, const runlist_element *rl,
|
||||
s64 ofs, const s64 pos, s64 count, void *b);
|
||||
|
||||
extern runlist_element *ntfs_runlists_merge(runlist_element *drl,
|
||||
runlist_element *srl);
|
||||
|
||||
extern runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol,
|
||||
const ATTR_RECORD *attr, runlist_element *old_rl);
|
||||
|
||||
extern int ntfs_get_nr_significant_bytes(const s64 n);
|
||||
|
||||
extern int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol,
|
||||
const runlist_element *rl, const VCN start_vcn, int max_size);
|
||||
|
||||
extern int ntfs_write_significant_bytes(u8 *dst, const u8 *dst_max,
|
||||
const s64 n);
|
||||
|
||||
extern int ntfs_mapping_pairs_build(const ntfs_volume *vol, u8 *dst,
|
||||
const int dst_len, const runlist_element *rl,
|
||||
const VCN start_vcn, runlist_element const **stop_rl);
|
||||
|
||||
extern int ntfs_rl_truncate(runlist **arl, const VCN start_vcn);
|
||||
|
||||
extern int ntfs_rl_sparse(runlist *rl);
|
||||
extern s64 ntfs_rl_get_compressed_size(ntfs_volume *vol, runlist *rl);
|
||||
|
||||
#ifdef NTFS_TEST
|
||||
int test_rl_main(int argc, char *argv[]);
|
||||
#endif
|
||||
|
||||
#endif /* defined _NTFS_RUNLIST_H */
|
||||
|
5184
source/security.c
Normal file
5184
source/security.c
Normal file
File diff suppressed because it is too large
Load Diff
357
source/security.h
Normal file
357
source/security.h
Normal file
@ -0,0 +1,357 @@
|
||||
/*
|
||||
* security.h - Exports for handling security/ACLs in NTFS.
|
||||
* Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2004 Anton Altaparmakov
|
||||
* Copyright (c) 2005-2006 Szabolcs Szakacsits
|
||||
* Copyright (c) 2007-2008 Jean-Pierre Andre
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_SECURITY_H
|
||||
#define _NTFS_SECURITY_H
|
||||
|
||||
#include "types.h"
|
||||
#include "layout.h"
|
||||
#include "inode.h"
|
||||
#include "dir.h"
|
||||
|
||||
#ifndef POSIXACLS
|
||||
#define POSIXACLS 0
|
||||
#endif
|
||||
|
||||
typedef u16 be16;
|
||||
typedef u32 be32;
|
||||
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
#define const_cpu_to_be16(x) ((((x) & 255L) << 8) + (((x) >> 8) & 255L))
|
||||
#define const_cpu_to_be32(x) ((((x) & 255L) << 24) + (((x) & 0xff00L) << 8) \
|
||||
+ (((x) >> 8) & 0xff00L) + (((x) >> 24) & 255L))
|
||||
#else
|
||||
#define const_cpu_to_be16(x) (x)
|
||||
#define const_cpu_to_be32(x) (x)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* item in the mapping list
|
||||
*/
|
||||
|
||||
struct MAPPING {
|
||||
struct MAPPING *next;
|
||||
int xid; /* linux id : uid or gid */
|
||||
SID *sid; /* Windows id : usid or gsid */
|
||||
int grcnt; /* group count (for users only) */
|
||||
gid_t *groups; /* groups which the user is member of */
|
||||
};
|
||||
|
||||
/*
|
||||
* Entry in the permissions cache
|
||||
* Note : this cache is not organized as a generic cache
|
||||
*/
|
||||
|
||||
struct CACHED_PERMISSIONS {
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
le32 inh_fileid;
|
||||
le32 inh_dirid;
|
||||
#if POSIXACLS
|
||||
struct POSIX_SECURITY *pxdesc;
|
||||
unsigned int pxdescsize:16;
|
||||
#endif
|
||||
unsigned int mode:12;
|
||||
unsigned int valid:1;
|
||||
} ;
|
||||
|
||||
/*
|
||||
* Entry in the permissions cache for directories with no security_id
|
||||
*/
|
||||
|
||||
struct CACHED_PERMISSIONS_LEGACY {
|
||||
struct CACHED_PERMISSIONS_LEGACY *next;
|
||||
struct CACHED_PERMISSIONS_LEGACY *previous;
|
||||
void *variable;
|
||||
size_t varsize;
|
||||
/* above fields must match "struct CACHED_GENERIC" */
|
||||
u64 mft_no;
|
||||
struct CACHED_PERMISSIONS perm;
|
||||
} ;
|
||||
|
||||
/*
|
||||
* Entry in the securid cache
|
||||
*/
|
||||
|
||||
struct CACHED_SECURID {
|
||||
struct CACHED_SECURID *next;
|
||||
struct CACHED_SECURID *previous;
|
||||
void *variable;
|
||||
size_t varsize;
|
||||
/* above fields must match "struct CACHED_GENERIC" */
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
unsigned int dmode;
|
||||
le32 securid;
|
||||
} ;
|
||||
|
||||
/*
|
||||
* Header of the security cache
|
||||
* (has no cache structure by itself)
|
||||
*/
|
||||
|
||||
struct CACHED_PERMISSIONS_HEADER {
|
||||
unsigned int last;
|
||||
/* statistics for permissions */
|
||||
unsigned long p_writes;
|
||||
unsigned long p_reads;
|
||||
unsigned long p_hits;
|
||||
} ;
|
||||
|
||||
/*
|
||||
* The whole permissions cache
|
||||
*/
|
||||
|
||||
struct PERMISSIONS_CACHE {
|
||||
struct CACHED_PERMISSIONS_HEADER head;
|
||||
struct CACHED_PERMISSIONS *cachetable[1]; /* array of variable size */
|
||||
} ;
|
||||
|
||||
/*
|
||||
* Security flags values
|
||||
*/
|
||||
|
||||
enum {
|
||||
SECURITY_DEFAULT, /* rely on fuse for permissions checking */
|
||||
SECURITY_RAW, /* force same ownership/permissions on files */
|
||||
SECURITY_ADDSECURIDS, /* upgrade old security descriptors */
|
||||
SECURITY_STATICGRPS, /* use static groups for access control */
|
||||
SECURITY_WANTED /* a security related option was present */
|
||||
} ;
|
||||
|
||||
/*
|
||||
* Security context, needed by most security functions
|
||||
*/
|
||||
|
||||
enum { MAPUSERS, MAPGROUPS, MAPCOUNT } ;
|
||||
|
||||
struct SECURITY_CONTEXT {
|
||||
ntfs_volume *vol;
|
||||
struct MAPPING *mapping[MAPCOUNT];
|
||||
struct PERMISSIONS_CACHE **pseccache;
|
||||
uid_t uid; /* uid of user requesting (not the mounter) */
|
||||
gid_t gid; /* gid of user requesting (not the mounter) */
|
||||
pid_t tid; /* thread id of thread requesting */
|
||||
mode_t umask; /* umask of requesting thread */
|
||||
} ;
|
||||
|
||||
#if POSIXACLS
|
||||
|
||||
/*
|
||||
* Posix ACL structures
|
||||
*/
|
||||
|
||||
struct POSIX_ACE {
|
||||
u16 tag;
|
||||
u16 perms;
|
||||
s32 id;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct POSIX_ACL {
|
||||
u8 version;
|
||||
u8 flags;
|
||||
u16 filler;
|
||||
struct POSIX_ACE ace[0];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct POSIX_SECURITY {
|
||||
mode_t mode;
|
||||
int acccnt;
|
||||
int defcnt;
|
||||
int firstdef;
|
||||
u16 tagsset;
|
||||
struct POSIX_ACL acl;
|
||||
} ;
|
||||
|
||||
/*
|
||||
* Posix tags, cpu-endian 16 bits
|
||||
*/
|
||||
|
||||
enum {
|
||||
POSIX_ACL_USER_OBJ = 1,
|
||||
POSIX_ACL_USER = 2,
|
||||
POSIX_ACL_GROUP_OBJ = 4,
|
||||
POSIX_ACL_GROUP = 8,
|
||||
POSIX_ACL_MASK = 16,
|
||||
POSIX_ACL_OTHER = 32,
|
||||
POSIX_ACL_SPECIAL = 64 /* internal use only */
|
||||
} ;
|
||||
|
||||
#define POSIX_ACL_EXTENSIONS (POSIX_ACL_USER | POSIX_ACL_GROUP | POSIX_ACL_MASK)
|
||||
|
||||
/*
|
||||
* Posix permissions, cpu-endian 16 bits
|
||||
*/
|
||||
|
||||
enum {
|
||||
POSIX_PERM_X = 1,
|
||||
POSIX_PERM_W = 2,
|
||||
POSIX_PERM_R = 4,
|
||||
POSIX_PERM_DENIAL = 64 /* internal use only */
|
||||
} ;
|
||||
|
||||
#define POSIX_VERSION 2
|
||||
|
||||
#endif
|
||||
|
||||
extern BOOL ntfs_guid_is_zero(const GUID *guid);
|
||||
extern char *ntfs_guid_to_mbs(const GUID *guid, char *guid_str);
|
||||
|
||||
/**
|
||||
* ntfs_sid_is_valid - determine if a SID is valid
|
||||
* @sid: SID for which to determine if it is valid
|
||||
*
|
||||
* Determine if the SID pointed to by @sid is valid.
|
||||
*
|
||||
* Return TRUE if it is valid and FALSE otherwise.
|
||||
*/
|
||||
static __inline__ BOOL ntfs_sid_is_valid(const SID *sid)
|
||||
{
|
||||
if (!sid || sid->revision != SID_REVISION ||
|
||||
sid->sub_authority_count > SID_MAX_SUB_AUTHORITIES)
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
extern int ntfs_sid_to_mbs_size(const SID *sid);
|
||||
extern char *ntfs_sid_to_mbs(const SID *sid, char *sid_str,
|
||||
size_t sid_str_size);
|
||||
extern void ntfs_generate_guid(GUID *guid);
|
||||
extern int ntfs_sd_add_everyone(ntfs_inode *ni);
|
||||
|
||||
extern le32 ntfs_security_hash(const SECURITY_DESCRIPTOR_RELATIVE *sd,
|
||||
const u32 len);
|
||||
|
||||
int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path,
|
||||
BOOL allowdef);
|
||||
int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx,
|
||||
ntfs_inode *ni, struct stat*);
|
||||
int ntfs_set_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, mode_t mode);
|
||||
BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni);
|
||||
int ntfs_allowed_access(struct SECURITY_CONTEXT *scx,
|
||||
ntfs_inode *ni, int accesstype);
|
||||
BOOL old_ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx,
|
||||
const char *path, int accesstype);
|
||||
|
||||
#if POSIXACLS
|
||||
le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx,
|
||||
uid_t uid, gid_t gid, ntfs_inode *dir_ni,
|
||||
mode_t mode, BOOL isdir);
|
||||
#else
|
||||
le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx,
|
||||
uid_t uid, gid_t gid, mode_t mode, BOOL isdir);
|
||||
#endif
|
||||
int ntfs_set_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
|
||||
uid_t uid, gid_t gid);
|
||||
int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx,
|
||||
ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode);
|
||||
#if POSIXACLS
|
||||
int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx,
|
||||
ntfs_inode *ni, uid_t uid, gid_t gid,
|
||||
mode_t mode, struct POSIX_SECURITY *pxdesc);
|
||||
#else
|
||||
int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx,
|
||||
ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode);
|
||||
#endif
|
||||
le32 ntfs_inherited_id(struct SECURITY_CONTEXT *scx,
|
||||
ntfs_inode *dir_ni, BOOL fordir);
|
||||
int ntfs_open_secure(ntfs_volume *vol);
|
||||
void ntfs_close_secure(struct SECURITY_CONTEXT *scx);
|
||||
|
||||
#if POSIXACLS
|
||||
|
||||
int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx,
|
||||
ntfs_inode *ni, uid_t uid, gid_t gid,
|
||||
ntfs_inode *dir_ni, mode_t mode);
|
||||
int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
|
||||
const char *name, char *value, size_t size);
|
||||
int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
|
||||
const char *name, const char *value, size_t size,
|
||||
int flags);
|
||||
int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
|
||||
const char *name);
|
||||
#endif
|
||||
|
||||
int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
|
||||
char *value, size_t size);
|
||||
int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
|
||||
const char *value, size_t size, int flags);
|
||||
|
||||
int ntfs_get_ntfs_attrib(ntfs_inode *ni, char *value, size_t size);
|
||||
int ntfs_set_ntfs_attrib(ntfs_inode *ni,
|
||||
const char *value, size_t size, int flags);
|
||||
|
||||
|
||||
/*
|
||||
* Security API for direct access to security descriptors
|
||||
* based on Win32 API
|
||||
*/
|
||||
|
||||
#define MAGIC_API 0x09042009
|
||||
|
||||
struct SECURITY_API {
|
||||
u32 magic;
|
||||
struct SECURITY_CONTEXT security;
|
||||
struct PERMISSIONS_CACHE *seccache;
|
||||
} ;
|
||||
|
||||
/*
|
||||
* The following constants are used in interfacing external programs.
|
||||
* They are not to be stored on disk and must be defined in their
|
||||
* native cpu representation.
|
||||
* When disk representation (le) is needed, use SE_DACL_PRESENT, etc.
|
||||
*/
|
||||
enum { OWNER_SECURITY_INFORMATION = 1,
|
||||
GROUP_SECURITY_INFORMATION = 2,
|
||||
DACL_SECURITY_INFORMATION = 4,
|
||||
SACL_SECURITY_INFORMATION = 8
|
||||
} ;
|
||||
|
||||
int ntfs_get_file_security(struct SECURITY_API *scapi,
|
||||
const char *path, u32 selection,
|
||||
char *buf, u32 buflen, u32 *psize);
|
||||
int ntfs_set_file_security(struct SECURITY_API *scapi,
|
||||
const char *path, u32 selection, const char *attr);
|
||||
int ntfs_get_file_attributes(struct SECURITY_API *scapi,
|
||||
const char *path);
|
||||
BOOL ntfs_set_file_attributes(struct SECURITY_API *scapi,
|
||||
const char *path, s32 attrib);
|
||||
BOOL ntfs_read_directory(struct SECURITY_API *scapi,
|
||||
const char *path, ntfs_filldir_t callback, void *context);
|
||||
int ntfs_read_sds(struct SECURITY_API *scapi,
|
||||
char *buf, u32 size, u32 offset);
|
||||
INDEX_ENTRY *ntfs_read_sii(struct SECURITY_API *scapi,
|
||||
INDEX_ENTRY *entry);
|
||||
INDEX_ENTRY *ntfs_read_sdh(struct SECURITY_API *scapi,
|
||||
INDEX_ENTRY *entry);
|
||||
struct SECURITY_API *ntfs_initialize_file_security(const char *device,
|
||||
int flags);
|
||||
BOOL ntfs_leave_file_security(struct SECURITY_API *scx);
|
||||
|
||||
int ntfs_get_usid(struct SECURITY_API *scapi, uid_t uid, char *buf);
|
||||
int ntfs_get_gsid(struct SECURITY_API *scapi, gid_t gid, char *buf);
|
||||
int ntfs_get_user(struct SECURITY_API *scapi, const SID *usid);
|
||||
int ntfs_get_group(struct SECURITY_API *scapi, const SID *gsid);
|
||||
|
||||
#endif /* defined _NTFS_SECURITY_H */
|
85
source/support.h
Normal file
85
source/support.h
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* support.h - Useful definitions and macros. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2004 Anton Altaparmakov
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_SUPPORT_H
|
||||
#define _NTFS_SUPPORT_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDDEF_H
|
||||
#include <stddef.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Our mailing list. Use this define to prevent typos in email address.
|
||||
*/
|
||||
#define NTFS_DEV_LIST "ntfs-3g-devel@lists.sf.net"
|
||||
|
||||
/*
|
||||
* Generic macro to convert pointers to values for comparison purposes.
|
||||
*/
|
||||
#ifndef p2n
|
||||
#define p2n(p) ((ptrdiff_t)((ptrdiff_t*)(p)))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The classic min and max macros.
|
||||
*/
|
||||
#ifndef min
|
||||
#define min(a,b) ((a) <= (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifndef max
|
||||
#define max(a,b) ((a) >= (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Useful macro for determining the offset of a struct member.
|
||||
*/
|
||||
#ifndef offsetof
|
||||
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simple bit operation macros. NOTE: These are NOT atomic.
|
||||
*/
|
||||
#define test_bit(bit, var) ((var) & (1 << (bit)))
|
||||
#define set_bit(bit, var) (var) |= 1 << (bit)
|
||||
#define clear_bit(bit, var) (var) &= ~(1 << (bit))
|
||||
|
||||
#define test_and_set_bit(bit, var) \
|
||||
({ \
|
||||
const BOOL old_state = test_bit(bit, var); \
|
||||
set_bit(bit, var); \
|
||||
old_state; \
|
||||
})
|
||||
|
||||
#define test_and_clear_bit(bit, var) \
|
||||
({ \
|
||||
const BOOL old_state = test_bit(bit, var); \
|
||||
clear_bit(bit, var); \
|
||||
old_state; \
|
||||
})
|
||||
|
||||
#endif /* defined _NTFS_SUPPORT_H */
|
||||
|
133
source/types.h
Normal file
133
source/types.h
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* types.h - Misc type definitions not related to on-disk structure.
|
||||
* Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2004 Anton Altaparmakov
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_TYPES_H
|
||||
#define _NTFS_TYPES_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#if HAVE_STDINT_H || !HAVE_CONFIG_H
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#ifdef GEKKO
|
||||
#include <gctypes.h>
|
||||
#include "compat.h"
|
||||
#else /* GEKKO */
|
||||
|
||||
typedef uint8_t u8; /* Unsigned types of an exact size */
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
|
||||
typedef int8_t s8; /* Signed types of an exact size */
|
||||
typedef int16_t s16;
|
||||
typedef int32_t s32;
|
||||
typedef int64_t s64;
|
||||
|
||||
#endif /* GEKKO */
|
||||
|
||||
typedef u16 le16;
|
||||
typedef u32 le32;
|
||||
typedef u64 le64;
|
||||
|
||||
/*
|
||||
* Declare sle{16,32,64} to be unsigned because we do not want sign extension
|
||||
* on BE architectures.
|
||||
*/
|
||||
typedef u16 sle16;
|
||||
typedef u32 sle32;
|
||||
typedef u64 sle64;
|
||||
|
||||
typedef u16 ntfschar; /* 2-byte Unicode character type. */
|
||||
#define UCHAR_T_SIZE_BITS 1
|
||||
|
||||
/*
|
||||
* Clusters are signed 64-bit values on NTFS volumes. We define two types, LCN
|
||||
* and VCN, to allow for type checking and better code readability.
|
||||
*/
|
||||
typedef s64 VCN;
|
||||
typedef sle64 leVCN;
|
||||
typedef s64 LCN;
|
||||
typedef sle64 leLCN;
|
||||
|
||||
/*
|
||||
* The NTFS journal $LogFile uses log sequence numbers which are signed 64-bit
|
||||
* values. We define our own type LSN, to allow for type checking and better
|
||||
* code readability.
|
||||
*/
|
||||
typedef s64 LSN;
|
||||
typedef sle64 leLSN;
|
||||
|
||||
/*
|
||||
* Cygwin has a collision between our BOOL and <windef.h>'s
|
||||
* As long as this file will be included after <windows.h> were fine.
|
||||
*/
|
||||
#ifndef GEKKO
|
||||
#ifndef _WINDEF_H
|
||||
/**
|
||||
* enum BOOL - These are just to make the code more readable...
|
||||
*/
|
||||
typedef enum {
|
||||
#ifndef FALSE
|
||||
FALSE = 0,
|
||||
#endif
|
||||
#ifndef NO
|
||||
NO = 0,
|
||||
#endif
|
||||
#ifndef ZERO
|
||||
ZERO = 0,
|
||||
#endif
|
||||
#ifndef TRUE
|
||||
TRUE = 1,
|
||||
#endif
|
||||
#ifndef YES
|
||||
YES = 1,
|
||||
#endif
|
||||
#ifndef ONE
|
||||
ONE = 1,
|
||||
#endif
|
||||
} BOOL;
|
||||
#endif /* defined _WINDEF_H */
|
||||
#endif /* defined GECKO */
|
||||
|
||||
/**
|
||||
* enum IGNORE_CASE_BOOL -
|
||||
*/
|
||||
typedef enum {
|
||||
CASE_SENSITIVE = 0,
|
||||
IGNORE_CASE = 1,
|
||||
} IGNORE_CASE_BOOL;
|
||||
|
||||
#define STATUS_OK (0)
|
||||
#define STATUS_ERROR (-1)
|
||||
#define STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT (-2)
|
||||
#define STATUS_KEEP_SEARCHING (-3)
|
||||
#define STATUS_NOT_FOUND (-4)
|
||||
|
||||
#endif /* defined _NTFS_TYPES_H */
|
||||
|
1438
source/unistr.c
Normal file
1438
source/unistr.c
Normal file
File diff suppressed because it is too large
Load Diff
118
source/unistr.h
Normal file
118
source/unistr.h
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* unistr.h - Exports for Unicode string handling. Originated from the Linux-NTFS
|
||||
* project.
|
||||
*
|
||||
* Copyright (c) 2000-2004 Anton Altaparmakov
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_UNISTR_H
|
||||
#define _NTFS_UNISTR_H
|
||||
|
||||
#include "types.h"
|
||||
#include "layout.h"
|
||||
|
||||
extern BOOL ntfs_names_are_equal(const ntfschar *s1, size_t s1_len,
|
||||
const ntfschar *s2, size_t s2_len, const IGNORE_CASE_BOOL ic,
|
||||
const ntfschar *upcase, const u32 upcase_size);
|
||||
|
||||
extern int ntfs_names_full_collate(const ntfschar *name1, const u32 name1_len,
|
||||
const ntfschar *name2, const u32 name2_len,
|
||||
const IGNORE_CASE_BOOL ic,
|
||||
const ntfschar *upcase, const u32 upcase_len);
|
||||
|
||||
extern int ntfs_ucsncmp(const ntfschar *s1, const ntfschar *s2, size_t n);
|
||||
|
||||
extern int ntfs_ucsncasecmp(const ntfschar *s1, const ntfschar *s2, size_t n,
|
||||
const ntfschar *upcase, const u32 upcase_size);
|
||||
|
||||
extern u32 ntfs_ucsnlen(const ntfschar *s, u32 maxlen);
|
||||
|
||||
extern ntfschar *ntfs_ucsndup(const ntfschar *s, u32 maxlen);
|
||||
|
||||
extern void ntfs_name_upcase(ntfschar *name, u32 name_len,
|
||||
const ntfschar *upcase, const u32 upcase_len);
|
||||
|
||||
extern void ntfs_name_locase(ntfschar *name, u32 name_len,
|
||||
const ntfschar *locase, const u32 locase_len);
|
||||
|
||||
extern void ntfs_file_value_upcase(FILE_NAME_ATTR *file_name_attr,
|
||||
const ntfschar *upcase, const u32 upcase_len);
|
||||
|
||||
extern int ntfs_ucstombs(const ntfschar *ins, const int ins_len, char **outs,
|
||||
int outs_len);
|
||||
extern int ntfs_mbstoucs(const char *ins, ntfschar **outs);
|
||||
|
||||
extern char *ntfs_uppercase_mbs(const char *low,
|
||||
const ntfschar *upcase, u32 upcase_len);
|
||||
|
||||
extern void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len);
|
||||
extern ntfschar *ntfs_locase_table_build(const ntfschar *uc, u32 uc_cnt);
|
||||
|
||||
extern ntfschar *ntfs_str2ucs(const char *s, int *len);
|
||||
|
||||
extern void ntfs_ucsfree(ntfschar *ucs);
|
||||
|
||||
extern BOOL ntfs_forbidden_chars(const ntfschar *name, int len);
|
||||
extern BOOL ntfs_collapsible_chars(ntfs_volume *vol,
|
||||
const ntfschar *shortname, int shortlen,
|
||||
const ntfschar *longname, int longlen);
|
||||
|
||||
extern int ntfs_set_char_encoding(const char *locale);
|
||||
|
||||
#if defined(__APPLE__) || defined(__DARWIN__)
|
||||
/**
|
||||
* Mac OS X only.
|
||||
*
|
||||
* Sets file name Unicode normalization form conversion on or off.
|
||||
* normalize=0 : Off
|
||||
* normalize=1 : On
|
||||
* If set to on, all filenames returned by ntfs-3g will be converted to the NFD
|
||||
* normalization form, while all filenames recieved by ntfs-3g will be converted to the NFC
|
||||
* normalization form. Since Windows and most other OS:es use the NFC form while Mac OS X
|
||||
* mostly uses NFD, this conversion increases compatibility between Mac applications and
|
||||
* NTFS-3G.
|
||||
*
|
||||
* @param normalize decides whether or not the string functions will do automatic filename
|
||||
* normalization when converting to and from UTF-8. 0 means normalization is disabled,
|
||||
* 1 means it is enabled.
|
||||
* @return -1 if the argument was invalid or an error occurred, 0 if all went well.
|
||||
*/
|
||||
extern int ntfs_macosx_normalize_filenames(int normalize);
|
||||
|
||||
/**
|
||||
* Mac OS X only.
|
||||
*
|
||||
* Normalizes the input string "utf8_string" to one of the normalization forms NFD or NFC.
|
||||
* The parameter "composed" decides whether output should be in composed, NFC, form
|
||||
* (composed == 1) or decomposed, NFD, form (composed == 0).
|
||||
* Input is assumed to be properly UTF-8 encoded and null-terminated. Output will be a newly
|
||||
* ntfs_calloc'ed string encoded in UTF-8. It is the callers responsibility to free(...) the
|
||||
* allocated string when it's no longer needed.
|
||||
*
|
||||
* @param utf8_string the input string, which may be in any normalization form.
|
||||
* @param target a pointer where the resulting string will be stored.
|
||||
* @param composed decides which composition form to normalize the input string to. 0 means
|
||||
* composed form (NFC), 1 means decomposed form (NFD).
|
||||
* @return -1 if the normalization failed for some reason, otherwise the length of the
|
||||
* normalized string stored in target.
|
||||
*/
|
||||
extern int ntfs_macosx_normalize_utf8(const char *utf8_string, char **target, int composed);
|
||||
#endif /* defined(__APPLE__) || defined(__DARWIN__) */
|
||||
|
||||
#endif /* defined _NTFS_UNISTR_H */
|
||||
|
1723
source/volume.c
Normal file
1723
source/volume.c
Normal file
File diff suppressed because it is too large
Load Diff
303
source/volume.h
Normal file
303
source/volume.h
Normal file
@ -0,0 +1,303 @@
|
||||
/*
|
||||
* volume.h - Exports for NTFS volume handling. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2004 Anton Altaparmakov
|
||||
* Copyright (c) 2004-2005 Richard Russon
|
||||
* Copyright (c) 2005-2006 Yura Pakhuchiy
|
||||
* Copyright (c) 2005-2009 Szabolcs Szakacsits
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program/include file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (in the main directory of the NTFS-3G
|
||||
* distribution in the file COPYING); if not, write to the Free Software
|
||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_VOLUME_H
|
||||
#define _NTFS_VOLUME_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_PARAM_H
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_MOUNT_H
|
||||
#include <sys/mount.h>
|
||||
#endif
|
||||
#ifdef HAVE_MNTENT_H
|
||||
#include <mntent.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Under Cygwin, DJGPP and FreeBSD we do not have MS_RDONLY,
|
||||
* so we define them ourselves.
|
||||
*/
|
||||
#ifndef MS_RDONLY
|
||||
#define MS_RDONLY 1
|
||||
#endif
|
||||
|
||||
#define MS_EXCLUSIVE 0x08000000
|
||||
|
||||
#ifndef MS_RECOVER
|
||||
#define MS_RECOVER 0x10000000
|
||||
#endif
|
||||
|
||||
#define MS_IGNORE_HIBERFILE 0x20000000
|
||||
|
||||
/* Forward declaration */
|
||||
typedef struct _ntfs_volume ntfs_volume;
|
||||
|
||||
#include "param.h"
|
||||
#include "types.h"
|
||||
#include "support.h"
|
||||
#include "device.h"
|
||||
#include "inode.h"
|
||||
#include "attrib.h"
|
||||
#include "index.h"
|
||||
|
||||
/**
|
||||
* enum ntfs_mount_flags -
|
||||
*
|
||||
* Flags returned by the ntfs_check_if_mounted() function.
|
||||
*/
|
||||
typedef enum {
|
||||
NTFS_MF_MOUNTED = 1, /* Device is mounted. */
|
||||
NTFS_MF_ISROOT = 2, /* Device is mounted as system root. */
|
||||
NTFS_MF_READONLY = 4, /* Device is mounted read-only. */
|
||||
} ntfs_mount_flags;
|
||||
|
||||
extern int ntfs_check_if_mounted(const char *file, unsigned long *mnt_flags);
|
||||
|
||||
typedef enum {
|
||||
NTFS_VOLUME_OK = 0,
|
||||
NTFS_VOLUME_SYNTAX_ERROR = 11,
|
||||
NTFS_VOLUME_NOT_NTFS = 12,
|
||||
NTFS_VOLUME_CORRUPT = 13,
|
||||
NTFS_VOLUME_HIBERNATED = 14,
|
||||
NTFS_VOLUME_UNCLEAN_UNMOUNT = 15,
|
||||
NTFS_VOLUME_LOCKED = 16,
|
||||
NTFS_VOLUME_RAID = 17,
|
||||
NTFS_VOLUME_UNKNOWN_REASON = 18,
|
||||
NTFS_VOLUME_NO_PRIVILEGE = 19,
|
||||
NTFS_VOLUME_OUT_OF_MEMORY = 20,
|
||||
NTFS_VOLUME_FUSE_ERROR = 21,
|
||||
NTFS_VOLUME_INSECURE = 22
|
||||
} ntfs_volume_status;
|
||||
|
||||
/**
|
||||
* enum ntfs_volume_state_bits -
|
||||
*
|
||||
* Defined bits for the state field in the ntfs_volume structure.
|
||||
*/
|
||||
typedef enum {
|
||||
NV_ReadOnly, /* 1: Volume is read-only. */
|
||||
NV_CaseSensitive, /* 1: Volume is mounted case-sensitive. */
|
||||
NV_LogFileEmpty, /* 1: $logFile journal is empty. */
|
||||
NV_ShowSysFiles, /* 1: Show NTFS metafiles. */
|
||||
NV_ShowHidFiles, /* 1: Show files marked hidden. */
|
||||
NV_HideDotFiles, /* 1: Set hidden flag on dot files */
|
||||
NV_Compression, /* 1: allow compression */
|
||||
} ntfs_volume_state_bits;
|
||||
|
||||
#define test_nvol_flag(nv, flag) test_bit(NV_##flag, (nv)->state)
|
||||
#define set_nvol_flag(nv, flag) set_bit(NV_##flag, (nv)->state)
|
||||
#define clear_nvol_flag(nv, flag) clear_bit(NV_##flag, (nv)->state)
|
||||
|
||||
#define NVolReadOnly(nv) test_nvol_flag(nv, ReadOnly)
|
||||
#define NVolSetReadOnly(nv) set_nvol_flag(nv, ReadOnly)
|
||||
#define NVolClearReadOnly(nv) clear_nvol_flag(nv, ReadOnly)
|
||||
|
||||
#define NVolCaseSensitive(nv) test_nvol_flag(nv, CaseSensitive)
|
||||
#define NVolSetCaseSensitive(nv) set_nvol_flag(nv, CaseSensitive)
|
||||
#define NVolClearCaseSensitive(nv) clear_nvol_flag(nv, CaseSensitive)
|
||||
|
||||
#define NVolLogFileEmpty(nv) test_nvol_flag(nv, LogFileEmpty)
|
||||
#define NVolSetLogFileEmpty(nv) set_nvol_flag(nv, LogFileEmpty)
|
||||
#define NVolClearLogFileEmpty(nv) clear_nvol_flag(nv, LogFileEmpty)
|
||||
|
||||
#define NVolShowSysFiles(nv) test_nvol_flag(nv, ShowSysFiles)
|
||||
#define NVolSetShowSysFiles(nv) set_nvol_flag(nv, ShowSysFiles)
|
||||
#define NVolClearShowSysFiles(nv) clear_nvol_flag(nv, ShowSysFiles)
|
||||
|
||||
#define NVolShowHidFiles(nv) test_nvol_flag(nv, ShowHidFiles)
|
||||
#define NVolSetShowHidFiles(nv) set_nvol_flag(nv, ShowHidFiles)
|
||||
#define NVolClearShowHidFiles(nv) clear_nvol_flag(nv, ShowHidFiles)
|
||||
|
||||
#define NVolHideDotFiles(nv) test_nvol_flag(nv, HideDotFiles)
|
||||
#define NVolSetHideDotFiles(nv) set_nvol_flag(nv, HideDotFiles)
|
||||
#define NVolClearHideDotFiles(nv) clear_nvol_flag(nv, HideDotFiles)
|
||||
|
||||
#define NVolCompression(nv) test_nvol_flag(nv, Compression)
|
||||
#define NVolSetCompression(nv) set_nvol_flag(nv, Compression)
|
||||
#define NVolClearCompression(nv) clear_nvol_flag(nv, Compression)
|
||||
|
||||
/*
|
||||
* NTFS version 1.1 and 1.2 are used by Windows NT4.
|
||||
* NTFS version 2.x is used by Windows 2000 Beta
|
||||
* NTFS version 3.0 is used by Windows 2000.
|
||||
* NTFS version 3.1 is used by Windows XP, 2003 and Vista.
|
||||
*/
|
||||
|
||||
#define NTFS_V1_1(major, minor) ((major) == 1 && (minor) == 1)
|
||||
#define NTFS_V1_2(major, minor) ((major) == 1 && (minor) == 2)
|
||||
#define NTFS_V2_X(major, minor) ((major) == 2)
|
||||
#define NTFS_V3_0(major, minor) ((major) == 3 && (minor) == 0)
|
||||
#define NTFS_V3_1(major, minor) ((major) == 3 && (minor) == 1)
|
||||
|
||||
#define NTFS_BUF_SIZE 8192
|
||||
|
||||
/**
|
||||
* struct _ntfs_volume - structure describing an open volume in memory.
|
||||
*/
|
||||
struct _ntfs_volume {
|
||||
union {
|
||||
struct ntfs_device *dev; /* NTFS device associated with
|
||||
the volume. */
|
||||
void *sb; /* For kernel porting compatibility. */
|
||||
};
|
||||
char *vol_name; /* Name of the volume. */
|
||||
unsigned long state; /* NTFS specific flags describing this volume.
|
||||
See ntfs_volume_state_bits above. */
|
||||
|
||||
ntfs_inode *vol_ni; /* ntfs_inode structure for FILE_Volume. */
|
||||
u8 major_ver; /* Ntfs major version of volume. */
|
||||
u8 minor_ver; /* Ntfs minor version of volume. */
|
||||
le16 flags; /* Bit array of VOLUME_* flags. */
|
||||
|
||||
u16 sector_size; /* Byte size of a sector. */
|
||||
u8 sector_size_bits; /* Log(2) of the byte size of a sector. */
|
||||
u32 cluster_size; /* Byte size of a cluster. */
|
||||
u32 mft_record_size; /* Byte size of a mft record. */
|
||||
u32 indx_record_size; /* Byte size of a INDX record. */
|
||||
u8 cluster_size_bits; /* Log(2) of the byte size of a cluster. */
|
||||
u8 mft_record_size_bits;/* Log(2) of the byte size of a mft record. */
|
||||
u8 indx_record_size_bits;/* Log(2) of the byte size of a INDX record. */
|
||||
|
||||
/* Variables used by the cluster and mft allocators. */
|
||||
u8 mft_zone_multiplier; /* Initial mft zone multiplier. */
|
||||
u8 full_zones; /* cluster zones which are full */
|
||||
s64 mft_data_pos; /* Mft record number at which to allocate the
|
||||
next mft record. */
|
||||
LCN mft_zone_start; /* First cluster of the mft zone. */
|
||||
LCN mft_zone_end; /* First cluster beyond the mft zone. */
|
||||
LCN mft_zone_pos; /* Current position in the mft zone. */
|
||||
LCN data1_zone_pos; /* Current position in the first data zone. */
|
||||
LCN data2_zone_pos; /* Current position in the second data zone. */
|
||||
|
||||
s64 nr_clusters; /* Volume size in clusters, hence also the
|
||||
number of bits in lcn_bitmap. */
|
||||
ntfs_inode *lcnbmp_ni; /* ntfs_inode structure for FILE_Bitmap. */
|
||||
ntfs_attr *lcnbmp_na; /* ntfs_attr structure for the data attribute
|
||||
of FILE_Bitmap. Each bit represents a
|
||||
cluster on the volume, bit 0 representing
|
||||
lcn 0 and so on. A set bit means that the
|
||||
cluster and vice versa. */
|
||||
|
||||
LCN mft_lcn; /* Logical cluster number of the data attribute
|
||||
for FILE_MFT. */
|
||||
ntfs_inode *mft_ni; /* ntfs_inode structure for FILE_MFT. */
|
||||
ntfs_attr *mft_na; /* ntfs_attr structure for the data attribute
|
||||
of FILE_MFT. */
|
||||
ntfs_attr *mftbmp_na; /* ntfs_attr structure for the bitmap attribute
|
||||
of FILE_MFT. Each bit represents an mft
|
||||
record in the $DATA attribute, bit 0
|
||||
representing mft record 0 and so on. A set
|
||||
bit means that the mft record is in use and
|
||||
vice versa. */
|
||||
|
||||
ntfs_inode *secure_ni; /* ntfs_inode structure for FILE $Secure */
|
||||
ntfs_index_context *secure_xsii; /* index for using $Secure:$SII */
|
||||
ntfs_index_context *secure_xsdh; /* index for using $Secure:$SDH */
|
||||
int secure_reentry; /* check for non-rentries */
|
||||
unsigned int secure_flags; /* flags, see security.h for values */
|
||||
|
||||
int mftmirr_size; /* Size of the FILE_MFTMirr in mft records. */
|
||||
LCN mftmirr_lcn; /* Logical cluster number of the data attribute
|
||||
for FILE_MFTMirr. */
|
||||
ntfs_inode *mftmirr_ni; /* ntfs_inode structure for FILE_MFTMirr. */
|
||||
ntfs_attr *mftmirr_na; /* ntfs_attr structure for the data attribute
|
||||
of FILE_MFTMirr. */
|
||||
|
||||
ntfschar *upcase; /* Upper case equivalents of all 65536 2-byte
|
||||
Unicode characters. Obtained from
|
||||
FILE_UpCase. */
|
||||
u32 upcase_len; /* Length in Unicode characters of the upcase
|
||||
table. */
|
||||
ntfschar *locase; /* Lower case equivalents of all 65536 2-byte
|
||||
Unicode characters. Only if option
|
||||
case_ignore is set. */
|
||||
|
||||
ATTR_DEF *attrdef; /* Attribute definitions. Obtained from
|
||||
FILE_AttrDef. */
|
||||
s32 attrdef_len; /* Size of the attribute definition table in
|
||||
bytes. */
|
||||
|
||||
s64 free_clusters; /* Track the number of free clusters which
|
||||
greatly improves statfs() performance */
|
||||
s64 free_mft_records; /* Same for free mft records (see above) */
|
||||
BOOL efs_raw; /* volume is mounted for raw access to
|
||||
efs-encrypted files */
|
||||
|
||||
#if CACHE_INODE_SIZE
|
||||
struct CACHE_HEADER *xinode_cache;
|
||||
#endif
|
||||
#if CACHE_NIDATA_SIZE
|
||||
struct CACHE_HEADER *nidata_cache;
|
||||
#endif
|
||||
#if CACHE_LOOKUP_SIZE
|
||||
struct CACHE_HEADER *lookup_cache;
|
||||
#endif
|
||||
#if CACHE_SECURID_SIZE
|
||||
struct CACHE_HEADER *securid_cache;
|
||||
#endif
|
||||
#if CACHE_LEGACY_SIZE
|
||||
struct CACHE_HEADER *legacy_cache;
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
extern const char *ntfs_home;
|
||||
|
||||
extern ntfs_volume *ntfs_volume_alloc(void);
|
||||
|
||||
extern ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev,
|
||||
unsigned long flags);
|
||||
|
||||
extern ntfs_volume *ntfs_device_mount(struct ntfs_device *dev,
|
||||
unsigned long flags);
|
||||
|
||||
extern ntfs_volume *ntfs_mount(const char *name, unsigned long flags);
|
||||
extern int ntfs_umount(ntfs_volume *vol, const BOOL force);
|
||||
|
||||
extern int ntfs_version_is_supported(ntfs_volume *vol);
|
||||
extern int ntfs_volume_check_hiberfile(ntfs_volume *vol, int verbose);
|
||||
extern int ntfs_logfile_reset(ntfs_volume *vol);
|
||||
|
||||
extern int ntfs_volume_write_flags(ntfs_volume *vol, const le16 flags);
|
||||
|
||||
extern int ntfs_volume_error(int err);
|
||||
extern void ntfs_mount_error(const char *vol, const char *mntpoint, int err);
|
||||
|
||||
extern int ntfs_volume_get_free_space(ntfs_volume *vol);
|
||||
|
||||
extern int ntfs_set_shown_files(ntfs_volume *vol,
|
||||
BOOL show_sys_files, BOOL show_hid_files, BOOL hide_dot_files);
|
||||
extern int ntfs_set_locale(void);
|
||||
extern int ntfs_set_ignore_case(ntfs_volume *vol);
|
||||
|
||||
#endif /* defined _NTFS_VOLUME_H */
|
||||
|
Loading…
Reference in New Issue
Block a user