├── MIT ├── Makefile ├── README.md ├── common ├── lostfiles.conf └── lostfiles.in └── doc └── lostfiles.1 /MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 2 | 3 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 4 | 5 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VERSION = 4.13 2 | PN = lostfiles 3 | 4 | PREFIX ?= /usr 5 | BINDIR = $(PREFIX)/bin 6 | MANDIR = $(PREFIX)/share/man/man1 7 | 8 | RM = rm 9 | SED = sed 10 | 11 | all: 12 | @echo -e '\033[1;32mSetting version\033[0m' 13 | @sed 's/@VERSION@/'$(VERSION)'/' common/$(PN).in > common/$(PN) 14 | 15 | install-bin: 16 | @echo -e '\033[1;32mInstalling main script, initd and config...\033[0m' 17 | install -Dm755 common/$(PN) "$(DESTDIR)$(BINDIR)/$(PN)" 18 | install -Dm644 common/$(PN).conf "$(DESTDIR)/etc/$(PN).conf" 19 | 20 | install-man: 21 | @echo -e '\033[1;32mInstalling manpage...\033[0m' 22 | install -Dm644 doc/$(PN).1 "$(DESTDIR)$(MANDIR)/$(PN).1" 23 | 24 | uninstall: 25 | $(RM) "$(DESTDIR)$(BINDIR)/$(PN)" 26 | $(RM) "$(DESTDIR)/etc/$(PN).conf" 27 | 28 | install: install-bin install-man 29 | 30 | clean: 31 | $(RM) -f common/$(PN) 32 | 33 | .PHONY: install-bin install-man uninstall install clean 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lostfiles 2 | Identifies files not owned and not created by any Arch Linux package. 3 | 4 | # Installation 5 | This package is provided in the Arch Linux official [extra](https://wiki.archlinux.org/title/Official_repositories#extra) repository. 6 | 7 | # Usage 8 | Run the script as root. See the included [man](https://github.com/graysky2/lostfiles/blob/master/doc/lostfiles.1) page for options and customization. Care should be taken in deciding which files might be extraneous, particularly when running in strict mode. 9 | 10 | # Dependencies 11 | This script is only for Linux distros that use pacman for package management. All dependencies are including the Arch Linux base group but are listed out here for general info: 12 | 13 | Executable | Arch package providing 14 | --- | --- 15 | bash | bash 16 | comm | coreutils 17 | find | findutils 18 | pacman | pacman 19 | sed | sed 20 | sort | coreutils 21 | tr | coreutils 22 | xargs | findutils 23 | 24 | ## Links 25 | Package: https://archlinux.org/packages/extra/any/lostfiles/ 26 | -------------------------------------------------------------------------------- /common/lostfiles.conf: -------------------------------------------------------------------------------- 1 | # each line prefixed by a plus sign (+) followed by an absolute path defines the 2 | # scope of where to look 3 | +/boot 4 | +/efi 5 | +/etc 6 | +/opt 7 | +/srv 8 | +/usr 9 | +/var 10 | 11 | # each line prefixed by a minus sign (-) followed by an absolute path defines 12 | # the excludes list 13 | -/boot/booster*.img 14 | -/boot/efi 15 | -/boot/EFI 16 | -/boot/grub 17 | -/boot/initramfs* 18 | -/boot/loader 19 | -/boot/lost+found 20 | -/boot/syslinux 21 | -/boot/vmlinuz* 22 | -/efi 23 | -/etc/adjtime 24 | -/etc/blkid.tab 25 | -/etc/ca-certificates 26 | -/etc/cni 27 | -/etc/conf.d 28 | -/etc/credstore 29 | -/etc/credstore.encrypted 30 | -/etc/cups/ppd/*.ppd 31 | -/etc/cups/ppd/*.ppd.O 32 | -/etc/cups/printers.conf.O 33 | -/etc/dfs 34 | -/etc/dhcpcd.duid 35 | -/etc/digitalocean 36 | -/etc/doas.conf 37 | -/etc/docker 38 | -/etc/easy-rsa 39 | -/etc/.etckeeper 40 | -/etc/fancontrol 41 | -/etc/fltk 42 | -/etc/fltk/fltk.org 43 | -/etc/fltk/fltk.org/fltk.prefs 44 | -/etc/fonts/conf.d 45 | -/etc/.git 46 | -/etc/group 47 | -/etc/group- 48 | -/etc/group.OLD 49 | -/etc/gshadow 50 | -/etc/gshadow- 51 | -/etc/gshadow.OLD 52 | -/etc/hostname 53 | -/etc/ld.so.cache 54 | -/etc/letsencrypt 55 | -/etc/locale.conf 56 | -/etc/localtime 57 | -/etc/lostfiles.conf 58 | -/etc/machine-id 59 | -/etc/mkinitcpio.d 60 | -/etc/network.d/ethernet-static 61 | -/etc/NetworkManager/system-connections 62 | -/etc/openvpn 63 | -/etc/os-release 64 | -/etc/pacman.d/gnupg 65 | -/etc/passwd 66 | -/etc/passwd- 67 | -/etc/passwd.OLD 68 | -/etc/ppp/ip-up.d/sslvpnroute.sh 69 | -/etc/ppp/netextender.pid 70 | -/etc/ppp/netextenderppp.pid 71 | -/etc/ppp/sslvpn.clientip 72 | -/etc/printcap 73 | -/etc/.pwd.lock 74 | -/etc/rc.digitalocean 75 | -/etc/resolv.conf.bak 76 | -/etc/samba/private/passdb.tdb 77 | -/etc/samba/private/secrets.tdb 78 | -/etc/samba/private/smbpasswd 79 | -/etc/samba/smb.conf 80 | -/etc/shadow 81 | -/etc/shadow- 82 | -/etc/shadow.OLD 83 | -/etc/ssh/ssh_host* 84 | -/etc/ssl 85 | -/etc/subgid- 86 | -/etc/subuid- 87 | -/etc/systemd/network 88 | -/etc/systemd/system 89 | -/etc/systemd/user 90 | -/etc/texmf/ls-R 91 | -/etc/texmf/web2c/updmap.cfg 92 | -/etc/udev/hwdb.bin 93 | -/etc/udev/rules.d/70-digitalocean-net.rules 94 | -/etc/.updated 95 | -/etc/vconsole.conf 96 | -/etc/wireguard 97 | -/etc/xml 98 | -/etc/zfs/zpool.cache 99 | -/opt/containerd 100 | -/opt/google-appengine-go/appengine/api/*.pyc 101 | -/srv 102 | -/usr/bin/__pycache__ 103 | -/usr/lib/gconv/gconv-modules.cache 104 | -/usr/lib/gdk-pixbuf-2.0/*/loaders.cache 105 | -/usr/lib/gio/modules/giomodule.cache 106 | -/usr/lib/gtk-2.0/*/immodules.cache 107 | -/usr/lib/gtk-3.0/*/immodules.cache 108 | -/usr/lib/libnetbrake.so.0 109 | -/usr/lib/locale/locale-archive 110 | -/usr/lib/udev/hwdb.bin 111 | -/usr/lib/virtualbox/ExtensionPacks/Oracle_VM_VirtualBox_Extension_Pack 112 | -/usr/local 113 | -/usr/share/applications/mimeinfo.cache 114 | -/usr/share/backintime 115 | -/usr/share/bleachbit/__pycache__ 116 | -/usr/share/dict/words 117 | -/usr/share/fonts/*/fonts.dir 118 | -/usr/share/fonts/*/fonts.scale 119 | -/usr/share/fonts/*/.uuid 120 | -/usr/share/fonts/.uuid 121 | -/usr/share/glib-2.0/schemas/gschemas.compiled 122 | -/usr/share/hplip/**/__pycache__ 123 | -/usr/share/icons/*/icon-theme.cache 124 | -/usr/share/info/dir 125 | -/usr/share/mime 126 | -/usr/share/.mono 127 | -/usr/share/.mono/certs 128 | -/usr/share/.mono/certs/Trust 129 | -/usr/share/.mono/new-certs 130 | -/usr/share/.mono/new-certs/Trust 131 | -/usr/share/texmf-dist/ls-R 132 | -/usr/share/texmf/ls-R 133 | -/usr/share/webapps/owncloud/data 134 | -/usr/var/cache 135 | -/usr/var/run 136 | -/var/abs 137 | -/var/cache 138 | -/var/db/sudo 139 | -/var/lib 140 | -/var/lock 141 | -/var/log 142 | -/var/lost+found 143 | -/var/run 144 | -/var/spool 145 | -/var/tmp 146 | -/var/.updated 147 | 148 | # more complicated exclusion filters can be expressed by a line beginning with 149 | # a dollar ($) sign, followed by command-line arguments to pass to find 150 | 151 | # exclude node modules outside of the npm subfolder 152 | $\( -not \( -path '/usr/lib/node_modules*' -and -not -path '/usr/lib/node_modules/npm/*' \) \) 153 | # exclude dkms module folder for the kernel versions currently installed to /boot/vmlinuz-linux* in /usr/lib/modules 154 | $\( `for kernel in /boot/vmlinuz-linux*; do version=$(file $kernel | cut -d ' ' -f 9); echo "-not ( -path /usr/lib/modules/$version -prune )";done` \) 155 | -------------------------------------------------------------------------------- /common/lostfiles.in: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eo pipefail 3 | 4 | echoerr() { 5 | # write to stderr 6 | cat <<< "$@" 1>&2; 7 | } 8 | 9 | if [ $UID != "0" ]; then 10 | echoerr "You must run this script as root." 11 | exit 1 12 | fi 13 | 14 | usage() { 15 | echo " lostfile @VERSION@" 16 | echo " Usage: $0 [-s] [-z] [-h]" 17 | echo " Calling without an option runs in relaxed mode sorting by name" 18 | echo " -h display this help" 19 | echo " -s optionally define strict mode" 20 | echo " -z optionally sort results by size" 21 | exit 0 22 | } 23 | 24 | # setup defaults which user can override with switches 25 | postprocess="sort" 26 | make_find_filter="make_relaxed_exclude_list" 27 | 28 | while getopts 'hsz' OPTION; do 29 | case "$OPTION" in 30 | z) 31 | postprocess="sort_by_size" 32 | ;; 33 | s) 34 | make_find_filter="make_strict_exclude_list" 35 | ;; 36 | h) 37 | usage 38 | ;; 39 | *) 40 | usage 41 | ;; 42 | esac 43 | done 44 | shift $((OPTIND -1)) 45 | 46 | # Sorts a list of file names by size 47 | sort_by_size() { 48 | tr '\n' '\0' | xargs -0 -n1 du -s | sort -rn -k1 49 | } 50 | 51 | # Converts a list of glob-like strings to a single regular expression 52 | # Only the * wildcard is supported. The rest of the characters are compared literally. 53 | miniglob_list_to_regex() { 54 | arr=("$@") 55 | str=$(IFS=$'\n' ; echo "${arr[*]}") 56 | echo -n "$str" | sed 's/[^^*]/[&]/g; s/\^/\\^/g; s/\*/.*/g' | tr '\n' '|' 57 | } 58 | 59 | # reads a list of paths from a configuration file at the specified path 60 | # and adds them to the global exclude list variable 61 | read_config() { 62 | if [ -f "$1" ]; then 63 | if grep -q '^[^#$ +-]' "$1"; then 64 | echoerr 'Invalid configuration file.' 65 | echoerr 'All lines in '"$1"' must start with #, + or -, $.' 66 | exit 1 67 | fi 68 | 69 | readarray -t include_list_from_file < <(grep '^+' "$1" | cut -c 2-) 70 | include_list=("${include_list[@]}" "${include_list_from_file[@]}") 71 | 72 | readarray -t exclude_list_from_file < <(grep '^-' "$1" | cut -c 2-) 73 | exclude_list=("${exclude_list[@]}" "${exclude_list_from_file[@]}") 74 | 75 | readarray -t custom_filters_list_from_file < <(grep '^\$' "$1" | cut -c 2-) 76 | custom_filters_list=("${custom_filters_list[@]}" "${custom_filters_list_from_file[@]}") 77 | fi 78 | } 79 | 80 | # Generates a find filter to implement the relaxed excluded paths list. 81 | # relaxed mode is more forgiving about hits, and excludes files generated by various apps. 82 | make_relaxed_exclude_list() { 83 | # read configuration files 84 | read_config "$(dirname "$0")/lostfiles.conf" 85 | read_config "/etc/lostfiles.conf" 86 | 87 | # build include list from configuration files 88 | find_filter=("${find_filter[@]}" "${include_list[@]}") 89 | 90 | # build a regex to exclude the paths specified in the configuration files 91 | exclude_list_regex=$(miniglob_list_to_regex "${exclude_list[@]}") 92 | 93 | find_filter=("${find_filter[@]}" -regextype posix-extended) 94 | find_filter=("${find_filter[@]}" \( -not \( -regex "|$exclude_list_regex|" -prune \) \)) 95 | 96 | # evaluate and add the custom filters (i.e. raw parameters to pass to find) 97 | for i in "${custom_filters_list[@]}"; do 98 | eval "temp=($i)" 99 | find_filter=("${find_filter[@]}" "${temp[@]}") 100 | done 101 | } 102 | 103 | # Do not exclude anything in strict mode, just add the default Arch paths 104 | make_strict_exclude_list() { 105 | find_filter=(/boot /efi /etc /opt /srv /usr /var) 106 | } 107 | 108 | $make_find_filter 109 | 110 | LC_ALL=C comm -13 \ 111 | <(LC_ALL=C pacman -Qlq | sed -e 's|/$||' | LC_ALL=C sort -u) \ 112 | <(LC_ALL=C find "${find_filter[@]}" 2>/dev/null | LC_ALL=C sort -u) | $postprocess 113 | 114 | # vim:set ts=2 sw=2 et: 115 | -------------------------------------------------------------------------------- /doc/lostfiles.1: -------------------------------------------------------------------------------- 1 | .\" Text automatically generated by txt2man 2 | .TH lostfile 1 "23 October 2020" "" "" 3 | .SH NAME 4 | \fBlostfiles \fP- Find orphaned files not owned by any packages. 5 | \fB 6 | .SH USAGE 7 | lostfiles [\fB-s\fP] [\fB-z\fP] 8 | .SH DESCRIPTION 9 | Lostfiles compares pacman's database of package owned files to the contents of actual filesystem and reports files that are not owned by a package. Useful for identifying non-native files. 10 | .SH OPTIONS 11 | 12 | \fB-s\fP 13 | .PP 14 | .nf 15 | .fam C 16 | Run in strict mode which aims to be more verbose ignoring less. 17 | 18 | .fam T 19 | .fi 20 | \fB-z\fP 21 | .PP 22 | .nf 23 | .fam C 24 | Print and sort by size of matches. 25 | 26 | .fam T 27 | .fi 28 | .SH USER-DEFINED EXCLUDES 29 | /etc/lostfiles.conf can be customized with a list of paths to include and exclude when running in relaxed mode. Each line beginning with a plus (+) or minus (-) sign, followed by an absolute path, will respectively include or exclude that path from the results. An asterisk (*) wildcard can be used as a placeholder matching any string. Additionally, more complicated exclusion filters can be expressed by a line beginning with a dollar ($) sign, followed by command-line arguments to pass to the find utility. Finally, lines beginning with a number sign (#) are interpreted as comments. 30 | .SH CONTRIBUTE 31 | Users wishing to contribute to this code, should fork and send a pull request. Source is freely available at: 32 | .RS 33 | .IP \(bu 3 34 | https://github.com/graysky2/lostfiles 35 | .SH AUTHORS 36 | Current maintainer: 37 | .IP \(bu 3 38 | graysky 39 | .PP 40 | Past major contributors: 41 | .IP \(bu 3 42 | Joan Bruguera (joanbrugueram AT gmail DOT com) 43 | .IP \(bu 3 44 | Mircea Bardac (dev AT mircea.bardac.net) 45 | .IP \(bu 3 46 | Modified by Jan Janssen, graysky, karol, asdffdsa, iceram, and others. 47 | --------------------------------------------------------------------------------