2020. febr. 17.

Quick&dirty moving of a billion files from a single directory

I had more than a billion files in a single directory on a ZFS (on Linux) volume.

If you got multiple computers with some simple naive software writing a small statistics log file every minute to a write-only shared directory, it is not hard to get there.

Note that this is obviously not a good solution for logging, and I don't endorse this practice, but this is what we had, and simple commands like the following were no longer completing in reasonable time:
cp /mnt/stat/data/2019_06_23_* /home/kalmi/incident_06_23/

Running ls or find was taking "forever", and was affecting the performance of the rest of the system. I had never considered before that ls by default has to load the whole directory into memory (or swap 👻) for sorting the entries.

It was getting quite inconvenient. Something needed to be done, because I wanted to do some processing on some of the existing files, and for that I needed commands to complete in reasonable time.

I decided to move the existing files into the following folder structure: year/month/day
I decided I would write a Python script to do the job of moving the existing files. The original filenames contained a timestamp, and that's all that is needed to determine the destination. I couldn't find a Python library that allowed for unsorted listing of such large directories in reasonable time without buffering the whole thing. I expected to be able to just iterate with glob.glob, but it wasn't performing as expected.

I already figured out earlier that ls with right flags can do that, so I just piped the output of ls to my Python script that actually moved the files, and the following solution was born:

cd /mnt/stat/data && ls -p -1 -f | grep -v / | python3 ../iter.py3

This previous line got added to cron, so that we wouldn't get to a billion files again.

The contents of iter.py3:

import fileinput
import string
import os
import errno

for line in fileinput.input():
    print(line.strip())
    if len(line.strip().split('_')) != 4:
        print('SKIPPED')
        continue
    name, date, hour, ns = line.strip().split('_')
    if len(date.split('-')) != 3:
        print('SKIPPED')
        continue
    year, month, day = date.split('-')
    if not year.isdigit() or not month.isdigit() or not day.isdigit():
        print('SKIPPED')
        continue
    try:
        os.mkdir(year)
    except OSError as e:
        if e.errno == errno.EEXIST:
            pass
        else:
            raise
    try:
        os.mkdir(os.path.join(year,month))
    except OSError as e:
        if e.errno == errno.EEXIST:
            pass
        else:
            raise
    try:
        os.mkdir(os.path.join(year,month,day))
    except OSError as e:
        if e.errno == errno.EEXIST:
            pass
        else:
            raise
    os.rename(line.strip(), os.path.join(year,month,day,line.strip()))

After running the script, the directory only contained a few folders, but running ls in that directory would still take a few seconds. It's interesting to me that the directory remained affected in this way. I guess ZFS didn't clean-up its now empty structures.

2019. dec. 12.

startRealHidden

Évekig rendszergazda voltam egy Internet Kávézóban, és egy napon úgy esett, hogy szükségem volt arra, hogy meglévő ablakos/konzolos alkalmazásokat teljesen rejtetten indítsak. Az is megeshetett, hogy ezek az alkalmazások futásuk során új ablakokat nyitottak még fel, és ezeknek se volt szabad megjelenniük.
Erre kész megoldást nem találtam.

Létezik egy Desktops nevű virtuális asztal kezelő Windowsra, ami működésében jelentősen eltér a többi virtuális asztal kezelőtől, márpedig abban, hogy nem úgy működik, hogy egyszerűen elrejti/megjeleníti az ablakokat, hanem ténylegesen külön Desktop-okon futnak az alkalmazások. Ez egy nem széles körben ismert fogalom, így nem mond sokat szerintem senkinek, de mindjárt kifejtem.
Ezek a Desktop-ok teljesen átjárhatatlan asztalok, és így a Desktops alkalmazásnak hátránya is van, mert nem lehet ablakokat mozgatni Desktop-ok közt, valamint például nem lehet 2 Desktop-on is egy-egy Firefox, mert a második nem indul el azzal a felkiáltással, hogy már már fut belőle egy, akivel nem tud kommunikálni.
(Igen, tudom, hogy különböző fx profilokat igen is el lehet indítani a 2 külön Desktop-on.)
Szokványos virtuális asztal kezelőknél egy új ablak mindig az aktuális asztalon jelenik meg, míg a Desktops esetén mindig ott jelenik meg, ahol az őt indító van.
Az UAC is egy ilyen Desktop-ot hoz létre mikor besötétíti a képernyőt, és megjeleníti azt az ablakot:
uac3
Ez miért jó az UAC-nak?
A gonosz programok általában szeretnének rendszergazdaként futni. Ehhez át kell magukat küzdeni egy ilyen megerősítőablakon. Ha ezt a megerősítőablakot meg tudnák kerülni, akkor megkerülnék. Egy futó programból nagyon egyszerű egy adott gomb fölé mozgatni az egeret, és kattintani egyet. Az UAC készítői természetesen gondoltak erre, és ha fel kell dobniuk egy ilyen ablakot, akkor csinálnak egy képernyőképet az asztalról, és aztán átváltanak egy másik Desktop-ra, ahol megjelenítik a korábban készült képet, és ezt az ablakot. A Desktop-ok átjárhatatlansága miatt a gonosz program nem tud az UAC által létrehozott Desktop-on egérmozgást programból előidézni. (Valamint azt hiszem, hogy másik user alatt is fut az UAC ablak.)

Térjünk is vissza az eredeti témánkra: Szóval szükségem volt valamire, ami rejtettem el tud indítani csúnya fekete ablakokat (vagy bármit).
Az általam készített megoldás a startRealHidden.
A startRealHidden a Desktops módszerét használja fel, és egy külön Desktopon indítja el a kívánt alkalmazást, amely így egyáltalán nem jelenik meg, és az általa indított dolgok se.
A lelke a dolognak (C#):
namespace startRealHidden
{
    class Program
    {
        static void Main(string[] args)
        {
            Desktop desktop = Desktop.CreateDesktop("myDesktop");
            Process p = desktop.CreateProcess(String.Join(" ", args));            
            p.WaitForExit();
        }
    }
}

A Desktop osztályt itt találhatjátok: http://www.codeproject.com/Articles/7666/Desktop-Switching

Használat: startRealHidden spawnBunchOfWindowsThatGoAwayOnTheirOwn.bat arg1 arg2 arg3

Egy jelentős limitáció van, és az az, hogy nem lehet egér/billentyűzet interakciót szimulálni nem aktív Desktop-on, tehát mondjuk egy interakciót szimuláló AutoIt szkript nem fog menni rejtetten.

Projekt letöltése

2016. aug. 5.

Quick&Dirty locally-trusted self-signed HTTPS on IIS on Win10

Run PowerShell as admin, then run:
New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -Subject my-domain.com -Type SSLServerAuthentication –DnsName my-domain.com,somesubdomain.my-domain.com
(replace my-domain.com with your own domain)

Go to the “Server Certificates” page in IIS Manager, right click on your new cert, select “View”, go to the “Details” tab, click the “Copy to file” button, just keep clicking next until it asks for a file name, then just save it somewhere.

Open the newly saved certificate, and click the “Install certificate” button, and install it into the current user’s “Trusted Root Certificate Authorities” store (Automatic store selection won’t do.).

Go back to IIS Manager, select your site, go to its Bindings, click “Add”, enter your domain, select “https” in the dropdown, and just select your cert.

Make sure to add the domain to the hosts file pointing  to 127.0.1!

2016. aug. 4.

Export all the drivers!

TIL you can export all your third-party drivers from PowerShell:
Export-WindowsDriver -Online -Destination e:\drivers_go_here

2015. dec. 20.

What goes into an IPFS multihash (in case of a file)

I started fooling around with a simple python multihash library, and I wanted to verify that it produced the same multihash as the go-ipfs reference implementation.

I created a hashme.txt with the following content: Hash me!
Then I called ”ipfs”, so that I would get a multihash:

> ipfs add hashme.txt
added QmNdTrvTHNM4tXjeoX1HD554eMTweTN91hf2ih6KKwXppo hashme.txt

I assumed that this hash would match the return value from the python multihash library when calling it like this:

>>> someMultihashLib.gen_hash(SHA_256, ‘Hash me!’)

I did not. My assumption was quite naive.

I wanted to see what the block (referenced by the QmNd… hash) actually contained, so I used ipfs block get to get the raw content of the block into a file:

> ipfs block get QmNdTrvTHNM4tXjeoX1HD554eMTweTN91hf2ih6KKwXppo > block.raw

Then I checked the content of this file from python:

>>> open('block.raw').read()
'\n\x0e\x08\x02\x12\x08Hash me!\x18\x08'

Okay, the “Hash me!” text is there as expected, but there is also some other stuff.

At this point after some light code browsing I found merkledag.proto, which I downloaded. So.. IPFS uses protobuf for serialization, so I got myself a protoc executable, and put it on my path. This is actually my first time using protobuf.

I assumed that the encapsulation could be an PBNode type from the previously found merkledag.proto file (It being a PBLink (referencing something else) wouldn’t make much sense, and this .proto file only contains these 2 types.), so here my first decoding attempt of block.raw:

> protoc --decode=PBNode merkledag.proto < block.raw
Data: "\010\002\022\010Hash me!\030\010"

It’s… um… a partial success. It could make sense. Instances of the PBNode type can only contain some number of Links (of type PBLink) and Data (of type bytes), and this one only contains data, but it is still encapsulated in something.

I tried the --decode_raw switch, and it seems like that protoc has decided by some heuristic that the bytes of the Data are not actually of just simple bytes, but something that it “understands”. Outer “1” key identifies PBNode’s Data. The inner 1,2,3 key-value pairs are a mystery for now.

>protoc --decode_raw < block.raw
1 {
  1: 2
  2: "Hash me!"
  3: 8
}

So I assumed the content of the Data field is also serialized with protobuf, but some other .proto file. I searched the go-ipfs codebase for .proto files, and the one that looked most promising was unixfs.proto.

It makes perfect sense since I actually added a file. This .proto file contains a Data type, that has the following field:

  • Type(key #1, and the value, “2” stands for File)
  • Data(key#2, yup, it’s “Hash me!”)
  • filesize(key#3, and yes, “Hash me!” string is actually 8 bytes long!)
  • and some other(s) fields that are irrelevant for this post…

So, to summarize: When calling the ”ipfs add” command, that data is first wrapped in the Data type from unixfs.proto, then the result is wrapped in a PBNode from merkledag.proto.

In hindsight, I should have used ”ipfs block put” to verify the python multihash implementation. The output of ”ipfs block put hashme.txt” matches the return value from the python library. Still, this was an interesting exploration.

Update: this post contains some inaccuracies and/or omissions, such as: protoc does not use heuristics, and data may be chunked when it is longer

2013. júl. 17.

How to get FlatOut 2 LAN multiplayer working over Hamachi

FlatOut 2 LAN multiplayer broadcasts UDP packets on the NIC with the lowest metric. (or at least the discovery part)

So all you got to do is set the "Hamachi" interface's metric to a small value, like 1.
Network Adapters->Hamachi->Properties->ipv4->Properties->Advanced->Metric

(And of course don't forget to take care of the firewall.)
(Also don't forget to change it back when you are finished playing.)

2013. máj. 31.

Teredo szimmetrikus NAT mögül

Hohó, még régebben egy csomót vacakoltam azzal, hogy Teredo-t valahogy életre keltsem az otthoni asztali gépemen, és most Windows 7-en sikerült.

A végeredmény

C:\Users\Kalmi>ping -6 ipv6.google.com

ipv6.l.google.com [2a00:1450:4016:802::1011] pingelése - 32 bájtnyi adattal:
Válasz 2a00:1450:4016:802::1011: idő=50 ms
Válasz 2a00:1450:4016:802::1011: idő=50 ms
Válasz 2a00:1450:4016:802::1011: idő=58 ms
Válasz 2a00:1450:4016:802::1011: idő=51 ms

2a00:1450:4016:802::1011 ping-statisztikája:
    Csomagok: küldött = 4, fogadott = 4, elveszett = 0
                        (0% veszteség),
Oda-vissza út ideje közelítőlegesen, milliszekundumban:
    minimum = 50ms, maximum = 58ms, átlag = 52ms

C:\Windows\system32>netsh interface teredo show state
Teredo-paraméterek
---------------------------------------------
Típus: client
Kiszolgálónév: teredo.niif.hu
Ügyfél frissítési időköze: 60 másodperc
Ügyfélport              : 34567
Állapot                 : qualified
Ügyféltípus             : teredo client
Hálózat                 : unmanaged
Hálózati címfordítás (NAT): restricted
Speciális hálózati címfordítás: UPNP: Nem, Portmegőrzés: Igen
Helyi leképezés: 192.168.100.57:34567
Külső NAT-leképezés: 86.59.189.185:34567

A folyamat

Az első lépés, amit elkövettem, az az volt, hogy megnyomtam uTorrent-ben a Settings>Connections>Install IPv6/Teredo gombot. Ez nem tudom, hogy pontosan mit csinált, de procexp-ben azt láttam, hogy netsh paracsot futtat. Gondolom csak engedélyezte a Teredo-t.

Ezen a ponton nem meglepő módon még nem működött a Teredo tunnel, mert a gépem szimmetrikus NAT mögött van, és azzal köztudottan nem működik együtt a Teredo.

Hasraütöttem, és kiválasztottam ügyfélportnak egy portot, amit beállítottam forwardingolásra a routeremen, valamint az asztali gépemen is beállítottam ügyfélportnak:
  netsh (in a console run as administrator)
  interface
  teredo
  set state clientport=XXXXX
  show state (just to check)
  exit

Ezen a ponton már működnie kell a ping -6 ipv6.google.com-nak, viszont alapból a Microsoft Teredo szervere van beállítva kiszolgálónak, ami nem ideális, mert messze van.
A következő paranccsal beállíthatjuk a NIIF magyar Teredo szerverét kiszolgálónak:
netsh interface teredo set state client teredo.niif.hu 60 34567