Blogity blog blog


Ką aš šiandien veikiau darbe, arba kaip nereikia programuoti

2009-04-23, rtfb

Vakar darbe pabaigiau vieną darbelį ir prieš dėdamas jį į revizijų kontrolę, nusprendžiau pabūti doras ir padaryti tai, ką mes vadinam TMR: Total Massive Rebuild. Tai veiksmas, kai perkompiliuoji visai visą produktą, kad įsitikinti, kad niekam nieko nesugadinai.

Nu cvs up, nu make clean, nu make all. Įprasti dalykai. Kompiliuojasi.

Tik kompiliuojasi neilgai, sustoja, nes sugriūna kažkuris unit testas. Galvoju, WTF? Bet aiškintis neturėjau laiko, tai užsiėmiau tuo šiandien.

Gana greitai išsiaiškinu kodėl griūna testas -- kolega neseniai padarė pakeitimą, visai gerą pakeitimą, dėl kurio šita problema ir turi išlįsti, o ne tyliai likti praignoruota. Viskas lyg ir tvarkoj. Bet vienas dalykas netvarkoj -- o kodėl man išlenda šita klaida, o kitiems ne?

Padebuginus paaiškėja, kad nesuveikia štai toks kodas:

mkdir ("%TEMP%/sub1/sub2/sub3/.../sub28/sub29");

(Tik vietoj %TEMP% ten buvo tikras kelias į temp direktoriją: C:\Users\Vytautas.Saltenis\AppData\Ir\Dar\Kažkas\Nepamenu\Kas\. Nu ir, aišku, vietoj daugtaškio visi trūkstami sub-sub-sub.)

Įtarimas aiškus -- kažkodėl viršija MAX_PATH. Betgi nusikopijuojam tą eilutę, pamatuojam jos ilgį ir matom, kad ne, neviršija. Jos ilgis 248. Va šitoj vietoj prasideda kondicija “O-ba…”.

Pirma mintis: gal aš neteisingai atsimenu kiek yra MAX_PATH? Pasitikrinu, 260. Reiškia teisingai, ne tas…

Tada tikrinam klaidos kodą. errno po mkdir() būna 2, kas yra ENOENT, ką dokumentacija iškilmingai išaiškina: “Path was not found.”. Blet. Koks dar not found? Aš gi sukurti bandau!

Gūglinam kaip ant Vindauso padarytas mkdir(). Randam, kad ten jis yra plonas wrapperis ant CreateDirectory() -- “gerai bent tiek, o ne atskira realizacija”, spėju pagalvoti.

OK, tad kaipgi galima priversti CreateDirectory() nesuveikti? Ten yra kažkoks antras parametras, kažkas su security, gal ten kažkas ne taip? Nu, debuginam visaip kaip išsijuosę, ir žiūrim, kad mkdir() padarytas maždaug va taip:

int mkdir (char *path)
{
    return CreateDirectory (path, NULL);
}

Blyn. Nu irgi nėra kam nesuveikti… Nebent tas NULL 20 metų veikė, o dabar neveikia. Vėjai.

Bet čia eiga pasisuka link atomazgos, prieinama prie <a href="http://msdn.microsoft.com/en-us/library/aa363855(VS.85).aspx">CreateDirectory()</a> dokumentacijos. Paskaitom prierašą prie pirmo parametro, ir liekam “в охуе”:

“There is a default string size limit for paths of 248 characters. This limit is related to how the CreateDirectory function parses paths.”

Bingo! Pasirodo, visgi susidūriau su limitu, pasiaiškinam kodėl kiti nesusidūrė, pataisom ir ilgai ir laimingai gyvenam. Boring.

The fun part! Kuo galvojo tas senovinis Microsoft darbuotojas, kuris parašė CreateDirectory()? Ar jis išvis galvojo? Ne, nu rimtai. Gaunasi taip: mūsų sistemoje apribojimas kelio ilgiui yra 260, bet aš chujovas programuotojas, dėl to nemoku suprogramuoti taip, kad būčiau suderinamas su likusia sistema ir padarysiu 248 ir dokumentacijoje parašysiu, kad aš chujovas programuotojas. Rimtai, taip išeina. Paskaitykit dar kartą citatą iš dokumentacijos:

“There is a default string size limit for paths of 248 characters. This limit is related to how impaired was the original author of CreateDirectory.”

Pakalbam apie tai su kolegomis, pažvengiam, aptariam, paspėliojam kodėl 248, o ne 246, kodėl išvis yra toks MAX_PATH ir kodėl jis 260, o ne 256 ir pan… Linksmiausia išsakyta versija tokia: o nefig tau kurti direktorijos, į kurią paskui netilps 8.3 failas! Dėl to ir MAX_PATH - 12 = 248. Skamba įtikinamai ;-)

Akylesni skaitytojai gal pastebėjo ir iki šiol nesupranta kodėl gi man neveikė? Juk mano kelio ilgis buvo 248, ir funkcija priima 248. Turi veikti. Cha cha! Nieko jūs nesuprantat! Chujovą funkciją reikia ne tik chujovai suprogramuoti, ją paskui dar reikia ir chujovai dokumentuoti! Kitaip gausis tik pusiau chujova funkcija. Iš tikro jinai veikia su keliais iki 247 simbolių ilgio imtinai, o dokumentatorius parašė kokio dydžio buferis ten viduje naudojamas keliui kartu su terminuojančiu nuliu laikyti.

Atrodytų, tokia vat nuotaikinga istorija. Ir maniau, kad toliau jau nėra kur, kad čia viršūnė. Nope, naivu. Betvarkydamas mūsų kodą, užklydau į kitos įdomios funkcijos <a href="http://msdn.microsoft.com/en-us/library/aa364991.aspx">GetTempFileName()</a> dokumentaciją. Skaitom pirmo parametro aprašymą:

“The directory path for the file name. […] The string cannot be longer than MAX_PATH–14 characters or GetTempFileName will fail. If this parameter is NULL, the function fails.”

Uch ty! Štai jums ir 246! Man dabar baisu į gūglą įvesti “MAX_PATH - 17”, bijau, kad ką nors suras :-)

Beje, plika akim tai sunkiai įžiūrima, bet ten dokumentacijoj parašyta ne “MAX_PATH-14”. Rimtai, nejuokauju. Ten ne minusas, ten tipografinis brūkšnys, “En dash” vadinasi. Ir jeigu nukopijuosi tą daiktą į kodą, gausi keistų pranešimų nuo kompiliatoriaus, versijų kontrolės sistemos ir kitų “suinteresuotų šalių”.

Va taip vat. Pyzdėc, gerbiamieji Redmondo programuotojai. Pyz-dėc.

 
Tagai: Programavimas

 
8 komentarai:

avatar

lol.
Kažkaip galvodavau, kad MAX_PATH yra lygus 256. O kam gali reikėt tokio ilgo patho ? Btw kam reikia daryti kažką windowsams jei taip nepatinka :) ?


avatar

nuostabi istorija :)


avatar

Andrius D.
2009-04-24 21:51

:-)
Kaip ir visada - blogą kodą paprasčiau paaiškinti, negu sutvarkyti ar parašyti iš karto gerą.
Abiem atvejais viskas yra gan paprasta:
260 = 256 + 4. 256 - tai gražus skaičius, o 4 - tai ESC-sekos ilgis: “\?\” - MSDN’o dokumentacijoje tai dažnai sutinkama seka, leidžianti išeiti už 256 simbolių ribą (kartais ;-)).
248 = 260 - 12 (8.3 failo vardas su tašku). Jeigu skaičiuoti tikrą ilgi (247) tai bent nesinori raudoti: apie backslash simboli pagalvojo, profai.


avatar

Gal tegu autorius eina toliau mokintis o ne rasinet nesamones…


avatar

fe, juokinga, užskaitom :-)


avatar

:D:D:D

lmao :)

emocijos superiškai prilygiagretintos prie debugo eigos. Made my (jeigu ne day, tai bent) evening :}

Vytai, dėkui. Tiesa, Wine projekto liaudis pastoviai su tokiais bajeriais susiduria ir jau nebesistebi. Tai tapo jų darbu :]

O čia Andriui D.: taip, viskas jau paaiškinta (blogposte) ir viskam savo priežastys, tik nesakyk, kad MS yra normalūs. Ten nėra komandos su vientisu požiūriu. Jei vieni kitus net MSDNe lazdavoja.


avatar

Santechnikas
2009-09-18 06:36

Iš viso, kas čia parašyta matosi, kad Tamsta esate pats kaltas, kadangi Tamstos vardo ir pavardės konkatenacija userio varde ir sudaro per ilgo kelio problemą, kurią galima išspręsti gana lengvai - t.y. pakeičiant Paties vardą ;)
Aš asmeniškai siūlau Jums “John Doe” :)


avatar

Santechnike, ne visai taip. Problemą išryškino labiau ne ilgas vardas (jis pas mane jau daug metų toks pat), o nauja sistema, kur kelias iki TEMP direktorijos ilgesnis. Ir naivus kodas, kuris tikisi, kad TEMP visada bus C:\Windows\Temp, pradėjo griuvinėti.

Dėl John Doe, ačiū už pasiūlymą, bet skirtingai nuo tamstos, aš linkęs pasirašinėti tuo, kuo esu, o ne vienokios ar kitokios formos anonimu ;-)

-rtfb


Mano nacis spam-filtras visus laiko botais.
Išspręskite šitą captcha jeigu jūs ne toks:

9 + 10 =