Jump to content











Photo
* * - - - 1 votes

improving grubinst and grubinst_gui in Windows and Linux


  • Please log in to reply
329 replies to this topic

#1 DavidB

DavidB

    Silver Member

  • Developer
  • 611 posts

Posted 01 January 2015 - 09:46 AM

Happy new year to everybody !

I'm trying to improve a bit "grubinst.exe" and "grubinst_gui.exe".
It's my first time I'm modifying other programmer(s)'s source code. I hope Bean, Tinybit and Chenall won't be upset :D

What I did so far:
grubinst_gui.exe:
1. The window is now a DS_MODALFRAME instead of a WS_POPUP, meaning it can't be resized anymore. Which it's a good thing, because resizing doesn't help the user, on the contrary.
2. In the Options groupbox I added 2 checkboxes: "Skip MBR test" and "Silent boot". The first one is adding "--skip-mbr-test" to the command line, the second is adding "--silent-boot".
3. If Device "File" is used, it is adding quotes to the path to file in the command line. This way you can use even a file name with spaces in it.
4. When the user is clicking on the first Refresh button, the Part List combobox is cleared.
5. When the user is choosing a disk in the first combobox, the Part list combobox is not just cleared; the disk will be scanned for partitions and the list will be added to that combobox.

5uo328.png

 

grubinst.exe:
1. Added 2 application flags: AFG_SILENT_BOOT (16384) and GFG_SILENT_BOOT (16).
2. Added this to "--help" switch: "--silent-boot It will display messages only on severe change(s) and/or error(s).".
3. If "--silent-boot" parameter is used, GFG_SILENT_BOOT is added to 0x80 in grub4dos MBR.
4. Removed duplicate "else if (! strcmp(argv[idx],"--skip-mbr-test")) afg|=AFG_SKIP_MBR_TEST;".

 

Now I have to actually modify the grub4dos MBR in the code.
2 ways:
1. In grubinst.c I can search for the messages in the grub_mbr variable and replace each of their chars with \0.
This is easy to implement but it is not so professional and the result will not be 100% complete.
2. Modify the asm code from grldrstart.S to not display those messages when the 4th bit in 0x80 byte is 1 (or to display the messages when the 4th bit in 0x80 byte is 0).
But this is not that easy.
From what I understand the compilation is done this way (it's simplified a bit):
1. grldrstart.S + ntfsbs.S >> grldr.mbr
2. bin2h.c + grldr.mbr >> grub_mbr.h
3. grubinst.c + grub_mbr.h >> grubinst.exe
Adding extra code to grldrstart.S means that grub_mbr variable will increase size and all bytes after the extra code will be shifted. Meaning probably I have to "resynchronize" it in grubinst.c
Also, since I'm not an assembler pro, I don't know how to modify this to work for the 4th bit:
 







testb $0x80, %cs:0x02 /* test bit 7 of the third byte */

jz 2f /* zero means boot prev-MBR first */

Attached File  original sources.7z   69.11KB   784 downloads

Any input is appreciated.
Thank you in advance.

Regards,
David

 

Later edit: attachments removed, you can find a better version at the the end of the thread...


  • Zoso likes this

#2 steve6375

steve6375

    Platinum Member

  • Developer
  • 6629 posts
  • Location:UK
  • Interests:computers, programming (masm,vb6,C,vbs), photography,TV,films,guitars
  •  
    United Kingdom

Posted 01 January 2015 - 11:54 AM

Grubinst_GUI does not display any drives unless run as Admin - maybe adding a manifest file would help?

Tooltips or a help screen would be nice to explain the options - or maybe just run grubinst with --help option so user can see switches?

Version number is still 1.3 (to avoid confusion can this be changed?)

grub_mbr final code size is < 30 sectors when installed - if it reaches 32 sectors there will be problems installing to a 32 SPT drive, so you need to be careful about increasing code size.

I have previously asked chenall et al. for silent switches for booting and for grldr and they were very resistant to it as they 'need' to see any messages etc. in case of errors, etc.  I would prefer it if there was a 'debug key' which the user could press on bootup (e.g. L-CTRL) which would display all messages but otherwise it was silent.

 

I modified the grubinst code a while ago to make the MBR more 'bootable' - the modified version of grubinst.exe is in RMPrepUSB.

Normal installation of grubdos to the MBR means that the MBR sector starts with a non-standard sequence of bytes - it seems that some BIOSes actually look at the first few bytes for a 'valid' jump instruction and thus these BIOSes won't boot to grub4dos!

Unfortunately, grub4dos uses some of the initial bytes in the MBR as flags (e.g. timeout, hotkey scancode, etc.) and so by adding the extra 'compatible' code bytes to the beginning of the MBR, it shifts these flag bytes along.

 

Standard MBR starts...

[Sector 0]  0MB
0000 EB 5E 80 05 20 39 FF FF - 00 00 00 00 00 00 00 00  ë^€. 9ÿÿ ........

i.e. these byte locations in MBR 2 - 7 had to be shifted by +2

  valueat(grub_mbr,2,unsigned char)=gfg;
  valueat(grub_mbr,3,unsigned char)=time_out;
  valueat(grub_mbr,4,unsigned short)=hot_key;
  valueat(grub_mbr,6,unsigned char)=def_drive;
  valueat(grub_mbr,7,unsigned char)=def_part;

 

 

So I had to modify the grubinst code to accommodate for this change too.

 

My modified MBR code starts...

[Sector 0]  0MB
0000 33 C0 EB 5C 80 00 20 39 - FF FF 00 00 00 00 00 00  3Àë\€. 9 ÿÿ......

I think maybe this is why people have more success when using RMPrepUSB to install grub4dos than just using the unmodified grubinst??

 

 

testing bit 4 would be

 

testb $0x10, %cs:0x02 /* test bit 4 of the third byte in MBR */

jz 2f   /* jump Forwards to label 2 if bit not set */

 

HTH

Steve



#3 steve6375

steve6375

    Platinum Member

  • Developer
  • 6629 posts
  • Location:UK
  • Interests:computers, programming (masm,vb6,C,vbs), photography,TV,films,guitars
  •  
    United Kingdom

Posted 01 January 2015 - 12:27 PM

P.S. When trying to install using grubinst_gui.exe  (your version or older version) - I get

 

Error - Can't  run the background program

 

I am using Win 8.1 64-bit system and running as Admin.

 

Similar to http://reboot.pro/to...vpe-ct-edition/



#4 DavidB

DavidB

    Silver Member

  • Developer
  • 611 posts

Posted 01 January 2015 - 09:04 PM

Grubinst_GUI does not display any drives unless run as Admin - maybe adding a manifest file would help?


Only partially, if you add one it will always start it as an admin no matter if you set in Properties or not. But the main problem will still be there...
Unfortunately, to properly get the information about drives and/or to write the MBR you need direct access.
 

Tooltips or a help screen would be nice to explain the options - or maybe just run grubinst with --help option so user can see switches?

 

If F1 is pressed then "grubinst --help" is launched, or, better, its output can be redirected and shown in a normal window.
 

Version number is still 1.3 (to avoid confusion can this be changed?)


Now it's 1.4.
 

I have previously asked chenall et al. for silent switches for booting and for grldr and they were very resistant to it as they 'need' to see any messages etc. in case of errors, etc. I would prefer it if there was a 'debug key' which the user could press on bootup (e.g. L-CTRL) which would display all messages but otherwise it was silent.


Another solution: the "normal" messages could be "stacked" in a buffer. When a severe error occurs, the buffer will be displayed + the current error.
 

P.S. When trying to install using grubinst_gui.exe (your version or older version) - I get

Error - Can't run the background program

I am using Win 8.1 64-bit system and running as Admin.

Similar to http://reboot.pro/to...vpe-ct-edition/


This error appears because grubinst_gui uses the old WinExec function instead of a newer one like CreateProcess(W).
Only CreateProcess(W) is able to start another process with the same security context as the calling proceess.
Btw, in VMUB I use CreateProcessW...
I will try to implement it also in grubinst_gui.


It will take some time to figure out how to properly modify grldrstart.S so, for the moment, I implemented the first way. Btw, it wasn't so easy as I thought...

 

Without silent boot:

 

with messages.png

 

With silent boot:

 

without messages.png


Another issue: I'm thinking of adding a new switch "--force-standard-mbr". Or at least to try to improve the filesystem type detection in get_fstype function.
Because it's not working properly. For example on all my USB flash drives and on the mounted virtual drives grubinst is saying "Unknown image type". No matter which combination of parameters I try, can't install grub4dos.
Unfortunately, RMPrepUSB is no different...
However, if in code I force the use of a standard MBR, all is fine. Grub4dos finally boots and the drives show no error.

 

 

LE: forgot to reactivate some code in compiled grubinst, reuploaded...

 

Later edit: attachments removed, you can find a better version at the the end of the thread...



#5 DavidB

DavidB

    Silver Member

  • Developer
  • 611 posts

Posted 02 January 2015 - 08:29 AM

I replaces WinExec with CreateProcess.
Could you please test to see if the problem is solved? Thank you.


The get_fstype function is making problems also in grubinst_gui:

Normal use:

Normal use.png

Forcing FST_MBR result for get_fstype function:
 

Force standard.png

 

Later edit: attachments removed, you can find a better version at the the end of the thread...



#6 steve6375

steve6375

    Platinum Member

  • Developer
  • 6629 posts
  • Location:UK
  • Interests:computers, programming (masm,vb6,C,vbs), photography,TV,films,guitars
  •  
    United Kingdom

Posted 02 January 2015 - 08:45 AM

Still same error...

Attached Thumbnails

  • CaptureGrubguibackgrnderror.JPG


#7 DavidB

DavidB

    Silver Member

  • Developer
  • 611 posts

Posted 02 January 2015 - 09:25 AM

That's strange, on my Windows 7 x64 (UAC activated, started as admin) works fine.

code:
 



STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si,sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
if( !CreateProcess(NULL,fn,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi))
{
PrintError(hWnd,IDS_EXEC_ERROR);
return;
}
WaitForSingleObject(pi.hProcess,INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);

Well, let's try some tweaks:
 



CreateProcess(NULL,fn,NULL,NULL,FALSE,CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi)


CreateProcess(NULL,fn,NULL,NULL,FALSE,DETACHED_PROCESS,NULL,NULL,&si,&pi)

Later edit: attachments removed, you can find a better version at the the end of the thread...



#8 DavidB

DavidB

    Silver Member

  • Developer
  • 611 posts

Posted 02 January 2015 - 02:27 PM

Meantime I have made other modifications:
- "Read only" checkbox is now "Force standard MBR";
- "Force standard MBR": in case of file system detection failure, it will use a standard MBR (I'm open to suggestions for a better name/explanation);
- "Test" button now starts grubinst with "--read-only" switch;
- on success, "grubinst.exe --read-only ..." displays "The MBR/BS can be successfully installed".
- if grubinst_gui fails to start grubinst.exe it will show an error code.

 

Later edit: attachments removed, you can find a better version at the the end of the thread...



#9 Wonko the Sane

Wonko the Sane

    The Finder

  • Advanced user
  • 13751 posts
  • Location:The Outside of the Asylum (gate is closed)
  •  
    Italy

Posted 02 January 2015 - 02:41 PM

- "Force standard MBR": in case of file system detection failure, it will use a standard MBR (I'm open to suggestions for a better name/explanation);

If you could actually explain to me (I am a bit tough :w00t: :ph34r:) what is the connection between filesystem and choice of MBR code :dubbio:, and you could define WHICH code is to be intended as "Standard", I may be able to help.  :unsure:

There are several if not "standard" at least "common" MBR codes, namely MS-DOS, FreeDOS, Windows NT/2K/XP, Windows Vista and 7 and 8/8.x, the Syslinux one which all share two characteristics:

  1. they are wholly contained in the first 512 bytes of the device ("proper" MBR, i.e. they don't make use of any of the "hidden sectors")
  2. their only scope is to chainload the bootsector of the primary partition marked as "boot" or "active" in the partition table.

:duff:

Wonko



#10 DavidB

DavidB

    Silver Member

  • Developer
  • 611 posts

Posted 02 January 2015 - 02:58 PM

Wonko, welcome to this topic :)

I was sure you'll want to help with this name/explanation...  ;)

 

The (original) connection was made actually by the author of get_fstype function by using this name ("get file system type") and by using as result FST_MBR, FST_MBR2 and so on.

The new option I added assign a FST_MBR result when this function fails. This way the rest of the code can continue to install the Grub4Dos MBR/BS.

I know that "standard MBR" is not that good to describe it but couldn't find a better one (for now).



#11 Wonko the Sane

Wonko the Sane

    The Finder

  • Advanced user
  • 13751 posts
  • Location:The Outside of the Asylum (gate is closed)
  •  
    Italy

Posted 02 January 2015 - 04:09 PM

Sure, we know that *someone* of the original Authors of the tool coded that, but - with all due respect to all involved - at least the way you explained it, it simply makes no sense whatsoever (to me at least).
Since usually the good guys that wrote those tools write very "sound" code, at least from a "phylosphical" point of view, I suspect that (while there is the possibility of a mistake by everone) that *somehow* the intention was not that of changing the MBR type depending on the filesystem(s) found but *something else*.

In any case, personally I see little advantages in that, if I were you I would rather write a grub4dos grldr.mbr installer (if you feel like one is needed) rather than fiddling with the old grubinst code.

Maybe useful, in any case, set aside for the moment the "fork" by Steve6375, which I am not familiar with, but that I presume won' t behave in any way differently if not for the slightly reduced space for an entirely optional and actually very rarely used "reserved" space for a BPB or similar, this is what I know that is needed to install grldr.mbr:
the whole grldr.mbr file is 9216 bytes, i.e. exactly 18 sectors
the file contains a "hole" on second sector, to hold "optionally" a copy of previous MBR
the first sector of it, the "proper" MBR, has three "holes".
  • a first one 0x58 bytes from offset 0x08 (0x10 in Steve's version) useful to hold (when needed - very, very rare case - a bootinfotable or a BPB block)
  • a second one for the disk signature from 0x1B8 (0x04 bytes in length)
  • a third one for the partition table from 0x1BE, 4 entries by 16 bytes
You can completely ignore the "first hole" in the "proper" MBR and just fill the #2 and #3 with *any* previous content, as stated in the read me:

Quote
******************************************************************************
*** grldr.mbr - How to write it to Master Boot Track of the hard disk ***
******************************************************************************

grldr.mbr contains code that can be used as Master Boot Record. The code is
responsible for searching all partitions for grldr and when found, loading it.
Currently supported partition types are: FAT12/FAT16/FAT32, NTFS, EXT2/EXT3.
Logical partitions in the extended partition are supported, provided that the
extended partition type is Microsoft-compatible. In fact, the Linux extended
partition type(0x85) is not fully tested for the search mechanism.

How to write GRLDR.MBR to the Master Boot Track of a hard disk?

First, read the Windows disk signature and partition information bytes
(72 bytes in total, from offset 0x01b8 to 0x01ff of the MBR sector), and put
them on the same range from offset 0x01b8 to 0x01ff of the beginning sector of
GRLDR.MBR.

Optionally, if the MBR in the hard disk is a single sector MBR created by
Microsoft FDISK, it may be copied onto the second sector of GRLDR.MBR.

The second sector of GRLDR.MBR is called "previous MBR". When GRLDR not found,
"previous MBR" will be started.

No other steps needed, after all necessary changes stated above have been made,
now simply write GRLDR.MBR on to the Master Boot Track. That's all.

Since you want to have anyway the "previous MBR" on second sector, a reasonable procedure is the following:
  • dump current MBR (whatever it is) to the second sector of a copy of grldr.mbr
  • copy 4 bytes from offset 0x1B8 of second sector to same offset on first sector
  • copy 64 bytes from offset 0x1BE of second sector to same offset on first sector
  • (since the two bytes at offset 0x1BC are never used and are normally 0000 and the last two bytes of the MBR MUST be the "Magic Bytes" 55AA, you can as well use the simplified single 72 bytes copy as in the read me)
  • write the thus modified copy of grldr.mbr to first 16 sectors of the disk
While you have the "copy of grldr.mbr" (i.e. before writing it to the MBR + hidden sectors) you may want to set the bunch of early "configuration bytes" to your likings (or according to the choices in the GUI) and these settings are also documented in the read me:

******************************************************************************
***               grldr.mbr - Details about the control bytes              ***
******************************************************************************

Six bytes can be used to control the boot process of GRLDR.MBR.

Offset	Length	Description
======	======	==============================================================
02h	1	bit0=1: disable the search for GRLDR on floppy
		bit0=0: enable the search for GRLDR on floppy

		bit1=1: disable the boot of PREVIOUS MBR with invalid
			partition table(usually an OS boot sector)
		bit1=0: enable the boot of PREVIOUS MBR with invalid
			partition table(usually an OS boot sector)

		bit2=1: disable the feature of unconditional entrance to
			the command-line(See below `--duce')
		bit2=0: enable the feature of unconditional entrance to
			the command-line(See below `--duce')

		bit3=1: disable geometry tune(See below `--chs-no-tune')
		bit3=0: enable geometry tune(See below `--chs-no-tune')

		bit4 - bit6: reserved

		bit7=1: try to boot PREVIOUS MBR after the search for GRLDR
		bit7=0: try to boot PREVIOUS MBR before the search for GRLDR

03h	1	timeout in seconds to wait for a key press. 0xff stands for
		waiting all the time(endless).

04h	2	hot-key code. high byte is scan code, low byte is ASCII code.
		the default value is 0x3920, which stands for the space bar.
		if this key is pressed, GRUB will be started prior to the boot
		of previous MBR. See "int 16 keyboard scan codes" below.

06h	1	preferred boot drive number, 0xff for no-drive
07h	1	preferred partition number, 0xff for whole drive

As I see it, anything else is NOT needed.

Let us remember that the scope is ONLY to install the grub4dos MBR code and nothing else (grubinst is not a "partitioning tool" or a "recovery tool" or an "increase boot chances" tool))

This could be done (excluded the configuration) in batch with just a bunch of commands *like* (using for the example dd for windows):

Quote
dd if=grldr.mbr of=mynew.mbr bs=512 count=16
dd if=\\.\Physicaldriven of=myold.mbr bs=512 count=1
dd if=myold.mbr of=mynew.mbr bs=1 count=512 seek=512
dd if=myold.mbr of=mynew.mbr bs=1 count=72 skip=440 seek=440
dd if=mynew.mbr of=\\.\Physicaldriven bs=512 count=16




Maybe you want - as an option - to provide an alternative "standard" MBR code, and if this is the case, I would suggest you to use the Syslinux one, which is surely freely redistributable.

:duff:
Wonko

#12 DavidB

DavidB

    Silver Member

  • Developer
  • 611 posts

Posted 02 January 2015 - 06:37 PM

Well, Wonko, thank you, it's always fun reading your posts :)



#13 Wonko the Sane

Wonko the Sane

    The Finder

  • Advanced user
  • 13751 posts
  • Location:The Outside of the Asylum (gate is closed)
  •  
    Italy

Posted 02 January 2015 - 07:11 PM

Well, Wonko, thank you, it's always fun reading your posts :)

Only trying - when possible - to have things go through Occam's Razor:

http://en.wikipedia....i/Occam's_razor

and see what gets on the other side unshaved/unscathed ;).

 

If you prefer :smiling9::

http://reboot.pro/to...uest-for-ddexe/

http://reboot.pro/to...ddexe/?p=143783

 

:duff:

Wonko



#14 DavidB

DavidB

    Silver Member

  • Developer
  • 611 posts

Posted 02 January 2015 - 08:34 PM

Later edit: attachments removed, you can find a better version at the the end of the thread...



#15 steve6375

steve6375

    Platinum Member

  • Developer
  • 6629 posts
  • Location:UK
  • Interests:computers, programming (masm,vb6,C,vbs), photography,TV,films,guitars
  •  
    United Kingdom

Posted 02 January 2015 - 08:37 PM

grubinst.gui - Still get 'Can't run the background program  Error 2'  - but now I also get the error on the Test button too!

 

Oh - wait a minute    :innocent: - I didnt have the  grubinst.exe in the same folder!!

Maybe you should test for that or give a better error message!



#16 DavidB

DavidB

    Silver Member

  • Developer
  • 611 posts

Posted 02 January 2015 - 08:41 PM

Error code 2 = "file not found".



#17 DavidB

DavidB

    Silver Member

  • Developer
  • 611 posts

Posted 03 January 2015 - 12:26 PM

Final version:

 

Later edit: attachments removed, you can find a better version at the the end of the thread...



#18 Wonko the Sane

Wonko the Sane

    The Finder

  • Advanced user
  • 13751 posts
  • Location:The Outside of the Asylum (gate is closed)
  •  
    Italy

Posted 03 January 2015 - 01:18 PM

Final version:

 

attachicon.gifbinaries.7z

 

attachicon.gifsources.7z

At first sight, very nice :thumbsup:

 

Very briefly tested, but it seems like working flawlessly on my XP SP2. :)

 

A few notes, if I may:

The command line grubinst does not provide any hint about usage.

The command line grubmenu has two options -r and -k but there is not a description of what they do.

It would be a good idea if the README_grubinst.txt was included also in the "binaries" archive and if a minimal README_grunbmenu.txt was added.

 

The GUI grubinst button text "flipping" between Install and Extract when the "save the embed(d)ed GRLDR.MBR" checkbox is checked/unchecked is not very "intuitive" IMHO.

 

:duff:

Wonko



#19 steve6375

steve6375

    Platinum Member

  • Developer
  • 6629 posts
  • Location:UK
  • Interests:computers, programming (masm,vb6,C,vbs), photography,TV,films,guitars
  •  
    United Kingdom

Posted 03 January 2015 - 01:29 PM

Would be great if you could add in my mod too which adds 2 bytes to the beginning of the MBR???

I have an old modified source file if you want to see it (it is a crude hack though!)



#20 steve6375

steve6375

    Platinum Member

  • Developer
  • 6629 posts
  • Location:UK
  • Interests:computers, programming (masm,vb6,C,vbs), photography,TV,films,guitars
  •  
    United Kingdom

Posted 03 January 2015 - 01:35 PM

The old version displayed the command line when Test button was used.

This was very useful. Can you show the command line first before running grubinst in Test mode?



#21 DavidB

DavidB

    Silver Member

  • Developer
  • 611 posts

Posted 03 January 2015 - 04:56 PM

The GUI grubinst button text "flipping" between Install and Extract when the "save the embed(d)ed GRLDR.MBR" checkbox is checked/unchecked is not very "intuitive" IMHO.


Maybe not 100%, but since the current operation (when it's checked) is to save/extract GRLDR.MBR, it's not logical to have to click on "Install".
 

Would be great if you could add in my mod too which adds 2 bytes to the beginning of the MBR???
I have an old modified source file if you want to see it (it is a crude hack though!)


I will take a look at the code. Just post it here.
But right now I'm thinking at a way to make grldr read the MBR byte where the options are stored (including --silent-boot) and not show those unnecessary messages on screen. The problem is I have to make it work in any situation: current MBR style + new grldr, new MBR style + current grldr, your current MBR style + new grldr and so on.
Plus I have to take into account the differences between grub4dos versions too...
Because of these I have chosen to modify grub_mbr in grubinst instead of modifying grldrstart.S (for the time being).
 

The old version displayed the command line when Test button was used.
This was very useful. Can you show the command line first before running grubinst in Test mode?

 

 

Later edit: attachments removed, you can find a better version at the the end of the thread...



#22 steve6375

steve6375

    Platinum Member

  • Developer
  • 6629 posts
  • Location:UK
  • Interests:computers, programming (masm,vb6,C,vbs), photography,TV,films,guitars
  •  
    United Kingdom

Posted 03 January 2015 - 05:56 PM

The original and modified files are here:

Let me know if link doesn't work.

Thanks

Steve



#23 steve6375

steve6375

    Platinum Member

  • Developer
  • 6629 posts
  • Location:UK
  • Interests:computers, programming (masm,vb6,C,vbs), photography,TV,films,guitars
  •  
    United Kingdom

Posted 03 January 2015 - 06:13 PM

new link



#24 DavidB

DavidB

    Silver Member

  • Developer
  • 611 posts

Posted 03 January 2015 - 06:38 PM

Can't download from both of them...



#25 steve6375

steve6375

    Platinum Member

  • Developer
  • 6629 posts
  • Location:UK
  • Interests:computers, programming (masm,vb6,C,vbs), photography,TV,films,guitars
  •  
    United Kingdom

Posted 03 January 2015 - 07:08 PM

odd? I can download using Incognito browser?

Try this






0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users